-
Notifications
You must be signed in to change notification settings - Fork 709
Events API implementation #4054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
dae55a5
initial commit
soumyadeepm04 078d75c
lint
soumyadeepm04 7df9ed2
Merge branch 'main' into eventapi
soumyadeepm04 f028535
resolve comments
soumyadeepm04 722fb54
Merge branch 'eventapi' of https://github.com/soumyadeepm04/opentelem…
soumyadeepm04 0b3eec2
Merge branch 'main' into eventapi
xrmx 32baf7c
Merge branch 'main' into eventapi
emdneto da611a8
updated CHANGELOG
soumyadeepm04 100826d
Merge branch 'eventapi' of https://github.com/soumyadeepm04/opentelem…
soumyadeepm04 79af742
Merge branch 'main' into eventapi
emdneto 2d186bd
Merge branch 'main' into eventapi
soumyadeepm04 7a4db6c
initial full implementation
soumyadeepm04 a991447
Merge branch 'eventapi' of https://github.com/soumyadeepm04/opentelem…
soumyadeepm04 7111579
Merge branch 'main' into eventapi
soumyadeepm04 1115efc
lint
soumyadeepm04 a20d31e
Merge branch 'eventapi' of https://github.com/soumyadeepm04/opentelem…
soumyadeepm04 e328c9d
lint
soumyadeepm04 0d1dd73
lint
soumyadeepm04 bfe1fb0
rename file
soumyadeepm04 235eec5
lint
soumyadeepm04 f25db47
Merge branch 'main' into eventapi
lzchen cfc3792
fix comments
soumyadeepm04 5589341
Merge branch 'eventapi' of https://github.com/soumyadeepm04/opentelem…
soumyadeepm04 2ff0a4e
fix lint
soumyadeepm04 1742aaa
fix comments
soumyadeepm04 f597da6
fix lint
soumyadeepm04 c3642ed
fix lint
soumyadeepm04 e379cb4
Merge branch 'main' into eventapi
lzchen f5928e8
Merge branch 'main' into eventapi
xrmx 3259a1b
Merge branch 'main' into eventapi
xrmx ad8244d
Update opentelemetry-api/src/opentelemetry/_events/__init__.py
soumyadeepm04 9e07117
fix attributes
soumyadeepm04 68831d3
Merge branch 'eventapi' of https://github.com/soumyadeepm04/opentelem…
soumyadeepm04 5b10d0b
fix lint
soumyadeepm04 2cf8bd9
Merge branch 'main' into eventapi
lzchen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
229 changes: 229 additions & 0 deletions
229
opentelemetry-api/src/opentelemetry/_events/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
# Copyright The OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from abc import ABC, abstractmethod | ||
from logging import getLogger | ||
from os import environ | ||
from typing import Any, Optional, cast | ||
|
||
from opentelemetry._logs import LogRecord | ||
from opentelemetry._logs.severity import SeverityNumber | ||
from opentelemetry.environment_variables import ( | ||
_OTEL_PYTHON_EVENT_LOGGER_PROVIDER, | ||
) | ||
from opentelemetry.trace.span import TraceFlags | ||
from opentelemetry.util._once import Once | ||
from opentelemetry.util._providers import _load_provider | ||
from opentelemetry.util.types import Attributes | ||
|
||
_logger = getLogger(__name__) | ||
|
||
|
||
class Event(LogRecord): | ||
|
||
def __init__( | ||
self, | ||
name: str, | ||
timestamp: Optional[int] = None, | ||
trace_id: Optional[int] = None, | ||
span_id: Optional[int] = None, | ||
trace_flags: Optional["TraceFlags"] = None, | ||
lzchen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
body: Optional[Any] = None, | ||
severity_number: Optional[SeverityNumber] = None, | ||
attributes: Optional[Attributes] = None, | ||
): | ||
attributes = attributes or {} | ||
event_attributes = {**attributes, "event.name": name} | ||
super().__init__( | ||
timestamp=timestamp, | ||
trace_id=trace_id, | ||
span_id=span_id, | ||
trace_flags=trace_flags, | ||
body=body, # type: ignore | ||
severity_number=severity_number, | ||
lzchen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
attributes=event_attributes, | ||
) | ||
self.name = name | ||
|
||
|
||
class EventLogger(ABC): | ||
xrmx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def __init__( | ||
self, | ||
name: str, | ||
version: Optional[str] = None, | ||
schema_url: Optional[str] = None, | ||
attributes: Optional[Attributes] = None, | ||
): | ||
self._name = name | ||
self._version = version | ||
self._schema_url = schema_url | ||
self._attributes = attributes | ||
|
||
@abstractmethod | ||
def emit(self, event: "Event") -> None: | ||
"""Emits a :class:`Event` representing an event.""" | ||
|
||
|
||
class NoOpEventLogger(EventLogger): | ||
|
||
def emit(self, event: Event) -> None: | ||
pass | ||
|
||
|
||
class ProxyEventLogger(EventLogger): | ||
def __init__( | ||
self, | ||
name: str, | ||
version: Optional[str] = None, | ||
schema_url: Optional[str] = None, | ||
attributes: Optional[Attributes] = None, | ||
): | ||
super().__init__( | ||
name=name, | ||
version=version, | ||
schema_url=schema_url, | ||
attributes=attributes, | ||
) | ||
self._real_event_logger: Optional[EventLogger] = None | ||
self._noop_event_logger = NoOpEventLogger(name) | ||
|
||
@property | ||
def _event_logger(self) -> EventLogger: | ||
if self._real_event_logger: | ||
return self._real_event_logger | ||
|
||
if _EVENT_LOGGER_PROVIDER: | ||
self._real_event_logger = _EVENT_LOGGER_PROVIDER.get_event_logger( | ||
self._name, | ||
self._version, | ||
self._schema_url, | ||
self._attributes, | ||
) | ||
return self._real_event_logger | ||
return self._noop_event_logger | ||
|
||
def emit(self, event: Event) -> None: | ||
self._event_logger.emit(event) | ||
|
||
|
||
class EventLoggerProvider(ABC): | ||
|
||
@abstractmethod | ||
def get_event_logger( | ||
self, | ||
name: str, | ||
version: Optional[str] = None, | ||
schema_url: Optional[str] = None, | ||
attributes: Optional[Attributes] = None, | ||
) -> EventLogger: | ||
"""Returns an EventLoggerProvider for use.""" | ||
|
||
|
||
class NoOpEventLoggerProvider(EventLoggerProvider): | ||
|
||
def get_event_logger( | ||
self, | ||
name: str, | ||
version: Optional[str] = None, | ||
schema_url: Optional[str] = None, | ||
attributes: Optional[Attributes] = None, | ||
) -> EventLogger: | ||
return NoOpEventLogger( | ||
name, version=version, schema_url=schema_url, attributes=attributes | ||
) | ||
|
||
|
||
class ProxyEventLoggerProvider(EventLoggerProvider): | ||
|
||
def get_event_logger( | ||
self, | ||
name: str, | ||
version: Optional[str] = None, | ||
schema_url: Optional[str] = None, | ||
attributes: Optional[Attributes] = None, | ||
) -> EventLogger: | ||
if _EVENT_LOGGER_PROVIDER: | ||
return _EVENT_LOGGER_PROVIDER.get_event_logger( | ||
name, | ||
version=version, | ||
schema_url=schema_url, | ||
attributes=attributes, | ||
) | ||
return ProxyEventLogger( | ||
name, | ||
version=version, | ||
schema_url=schema_url, | ||
attributes=attributes, | ||
) | ||
|
||
|
||
_EVENT_LOGGER_PROVIDER_SET_ONCE = Once() | ||
_EVENT_LOGGER_PROVIDER: Optional[EventLoggerProvider] = None | ||
_PROXY_EVENT_LOGGER_PROVIDER = ProxyEventLoggerProvider() | ||
|
||
|
||
def get_event_logger_provider() -> EventLoggerProvider: | ||
|
||
global _EVENT_LOGGER_PROVIDER # pylint: disable=global-variable-not-assigned | ||
if _EVENT_LOGGER_PROVIDER is None: | ||
if _OTEL_PYTHON_EVENT_LOGGER_PROVIDER not in environ: | ||
return _PROXY_EVENT_LOGGER_PROVIDER | ||
|
||
event_logger_provider: EventLoggerProvider = _load_provider( # type: ignore | ||
_OTEL_PYTHON_EVENT_LOGGER_PROVIDER, "event_logger_provider" | ||
) | ||
|
||
_set_event_logger_provider(event_logger_provider, log=False) | ||
|
||
return cast("EventLoggerProvider", _EVENT_LOGGER_PROVIDER) | ||
|
||
|
||
def _set_event_logger_provider( | ||
event_logger_provider: EventLoggerProvider, log: bool | ||
) -> None: | ||
def set_elp() -> None: | ||
global _EVENT_LOGGER_PROVIDER # pylint: disable=global-statement | ||
_EVENT_LOGGER_PROVIDER = event_logger_provider | ||
|
||
did_set = _EVENT_LOGGER_PROVIDER_SET_ONCE.do_once(set_elp) | ||
|
||
if log and did_set: | ||
_logger.warning( | ||
"Overriding of current EventLoggerProvider is not allowed" | ||
) | ||
|
||
|
||
soumyadeepm04 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def set_event_logger_provider( | ||
event_logger_provider: EventLoggerProvider, | ||
) -> None: | ||
|
||
_set_event_logger_provider(event_logger_provider, log=True) | ||
|
||
|
||
def get_event_logger( | ||
name: str, | ||
version: Optional[str] = None, | ||
schema_url: Optional[str] = None, | ||
attributes: Optional[Attributes] = None, | ||
event_logger_provider: Optional[EventLoggerProvider] = None, | ||
) -> "EventLogger": | ||
if event_logger_provider is None: | ||
event_logger_provider = get_event_logger_provider() | ||
return event_logger_provider.get_event_logger( | ||
name, | ||
version, | ||
schema_url, | ||
attributes, | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import unittest | ||
|
||
from opentelemetry._events import Event | ||
|
||
|
||
class TestEvent(unittest.TestCase): | ||
def test_event(self): | ||
event = Event("example", 123, attributes={"key": "value"}) | ||
self.assertEqual(event.name, "example") | ||
self.assertEqual(event.timestamp, 123) | ||
self.assertEqual( | ||
event.attributes, {"key": "value", "event.name": "example"} | ||
) |
47 changes: 47 additions & 0 deletions
47
opentelemetry-api/tests/events/test_event_logger_provider.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# type:ignore | ||
import unittest | ||
from unittest.mock import Mock, patch | ||
|
||
import opentelemetry._events as events | ||
from opentelemetry._events import ( | ||
get_event_logger_provider, | ||
set_event_logger_provider, | ||
) | ||
from opentelemetry.test.globals_test import EventsGlobalsTest | ||
|
||
|
||
class TestGlobals(EventsGlobalsTest, unittest.TestCase): | ||
def test_set_event_logger_provider(self): | ||
elp_mock = Mock() | ||
# pylint: disable=protected-access | ||
self.assertIsNone(events._EVENT_LOGGER_PROVIDER) | ||
set_event_logger_provider(elp_mock) | ||
self.assertIs(events._EVENT_LOGGER_PROVIDER, elp_mock) | ||
self.assertIs(get_event_logger_provider(), elp_mock) | ||
|
||
def test_get_event_logger_provider(self): | ||
# pylint: disable=protected-access | ||
self.assertIsNone(events._EVENT_LOGGER_PROVIDER) | ||
|
||
self.assertIsInstance( | ||
get_event_logger_provider(), events.ProxyEventLoggerProvider | ||
) | ||
|
||
events._EVENT_LOGGER_PROVIDER = None | ||
|
||
with patch.dict( | ||
"os.environ", | ||
{ | ||
"OTEL_PYTHON_EVENT_LOGGER_PROVIDER": "test_event_logger_provider" | ||
}, | ||
): | ||
|
||
with patch("opentelemetry._events._load_provider", Mock()): | ||
with patch( | ||
"opentelemetry._events.cast", | ||
Mock(**{"return_value": "test_event_logger_provider"}), | ||
): | ||
self.assertEqual( | ||
get_event_logger_provider(), | ||
"test_event_logger_provider", | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# pylint: disable=W0212,W0222,W0221 | ||
import typing | ||
import unittest | ||
|
||
import opentelemetry._events as events | ||
from opentelemetry.test.globals_test import EventsGlobalsTest | ||
from opentelemetry.util.types import Attributes | ||
|
||
|
||
class TestProvider(events.NoOpEventLoggerProvider): | ||
def get_event_logger( | ||
self, | ||
name: str, | ||
version: typing.Optional[str] = None, | ||
schema_url: typing.Optional[str] = None, | ||
attributes: typing.Optional[Attributes] = None, | ||
) -> events.EventLogger: | ||
return LoggerTest(name) | ||
|
||
|
||
class LoggerTest(events.NoOpEventLogger): | ||
def emit(self, event: events.Event) -> None: | ||
pass | ||
|
||
|
||
class TestProxy(EventsGlobalsTest, unittest.TestCase): | ||
def test_proxy_logger(self): | ||
provider = events.get_event_logger_provider() | ||
# proxy provider | ||
self.assertIsInstance(provider, events.ProxyEventLoggerProvider) | ||
|
||
# provider returns proxy logger | ||
event_logger = provider.get_event_logger("proxy-test") | ||
self.assertIsInstance(event_logger, events.ProxyEventLogger) | ||
|
||
# set a real provider | ||
events.set_event_logger_provider(TestProvider()) | ||
|
||
# get_logger_provider() now returns the real provider | ||
self.assertIsInstance(events.get_event_logger_provider(), TestProvider) | ||
|
||
# logger provider now returns real instance | ||
self.assertIsInstance( | ||
events.get_event_logger_provider().get_event_logger("fresh"), | ||
LoggerTest, | ||
) | ||
|
||
# references to the old provider still work but return real logger now | ||
real_logger = provider.get_event_logger("proxy-test") | ||
self.assertIsInstance(real_logger, LoggerTest) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.