Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5261.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for configuring the instrumentation scope used by the SDK logger created by `LoggingHandler`.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from logging import getLogger
from os import environ
from time import time_ns
from typing import cast, overload
from typing import Any, cast, overload

from typing_extensions import deprecated

Expand Down Expand Up @@ -227,11 +227,13 @@ def __init__( # pylint: disable=super-init-not-called
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope: Any | None = None,
):
self._name = name
self._version = version
self._schema_url = schema_url
self._attributes = attributes
self._instrumentation_scope = instrumentation_scope
self._real_logger: Logger | None = None
self._noop_logger = NoOpLogger(name)

Expand All @@ -241,11 +243,20 @@ def _logger(self) -> Logger:
return self._real_logger

if _LOGGER_PROVIDER:
if self._instrumentation_scope is None:
self._real_logger = _LOGGER_PROVIDER.get_logger(
self._name,
self._version,
self._schema_url,
self._attributes,
)
return self._real_logger
self._real_logger = _LOGGER_PROVIDER.get_logger(
self._name,
self._version,
self._schema_url,
self._attributes,
self._instrumentation_scope,
)
return self._real_logger
return self._noop_logger
Expand Down Expand Up @@ -313,6 +324,7 @@ def get_logger(
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope: Any | None = None,
) -> Logger:
"""Returns a `Logger` for use by the given instrumentation library.

Expand Down Expand Up @@ -352,8 +364,10 @@ def get_logger(
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope: Any | None = None,
) -> Logger:
"""Returns a NoOpLogger."""
_ = instrumentation_scope
return NoOpLogger(
name, version=version, schema_url=schema_url, attributes=attributes
)
Expand All @@ -366,19 +380,29 @@ def get_logger(
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope: Any | None = None,
) -> Logger:
if _LOGGER_PROVIDER:
if instrumentation_scope is None:
return _LOGGER_PROVIDER.get_logger(
name,
version=version,
schema_url=schema_url,
attributes=attributes,
)
return _LOGGER_PROVIDER.get_logger(
name,
version=version,
schema_url=schema_url,
attributes=attributes,
instrumentation_scope=instrumentation_scope,
)
return ProxyLogger(
name,
version=version,
schema_url=schema_url,
attributes=attributes,
instrumentation_scope=instrumentation_scope,
)


Expand Down Expand Up @@ -429,6 +453,7 @@ def get_logger(
logger_provider: LoggerProvider | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope: Any | None = None,
) -> Logger:
"""Returns a `Logger` for use within a python process.

Expand All @@ -439,9 +464,17 @@ def get_logger(
"""
if logger_provider is None:
logger_provider = get_logger_provider()
if instrumentation_scope is None:
return logger_provider.get_logger(
instrumenting_module_name,
instrumenting_library_version,
schema_url,
attributes,
)
return logger_provider.get_logger(
instrumenting_module_name,
instrumenting_library_version,
schema_url,
attributes,
instrumentation_scope,
)
58 changes: 58 additions & 0 deletions opentelemetry-api/tests/logs/test_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# pylint: disable=W0212,W0222,W0221
import unittest
from typing import cast
from unittest.mock import Mock

import opentelemetry._logs._internal as _logs_internal
Expand All @@ -18,7 +19,36 @@ def get_logger(
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope=None,
) -> _logs.Logger:
_ = instrumentation_scope
return LoggerTest(name)


class OldSignatureLoggerProvider:
def get_logger( # pylint: disable=no-self-use
self,
name: str,
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
) -> _logs.Logger:
return LoggerTest(name)


class LoggerProviderTest(_logs.NoOpLoggerProvider):
def __init__(self):
self.instrumentation_scope = None

def get_logger(
self,
name: str,
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope=None,
) -> _logs.Logger:
self.instrumentation_scope = instrumentation_scope
return LoggerTest(name)


Expand Down Expand Up @@ -65,6 +95,34 @@ def test_proxy_logger(self):
real_logger = provider.get_logger("proxy-test")
self.assertIsInstance(real_logger, LoggerTest)

def test_proxy_logger_instrumentation_scope(self):
provider = _logs.get_logger_provider()
instrumentation_scope = object()

logger = provider.get_logger(
"proxy-test", instrumentation_scope=instrumentation_scope
)

logger_provider = LoggerProviderTest()
_logs.set_logger_provider(logger_provider)

proxy_logger = cast(_logs_internal.ProxyLogger, logger)
self.assertIsInstance(proxy_logger._logger, LoggerTest)
self.assertIs(
logger_provider.instrumentation_scope, instrumentation_scope
)

def test_get_logger_works_with_old_signature_provider(self):
logger_provider = cast(
_logs.LoggerProvider, OldSignatureLoggerProvider()
)

logger = _logs.get_logger(
"proxy-test", logger_provider=logger_provider
)

self.assertIsInstance(logger, LoggerTest)

def test_proxy_logger_forwards_record_with_exception(self):
logger = _logs_internal.ProxyLogger("proxy-test")
logger._real_logger = Mock(spec=LoggerTest("proxy-test"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,9 +544,11 @@ def __init__(
self,
level: int = logging.NOTSET,
logger_provider: APILoggerProvider | None = None,
instrumentation_scope: InstrumentationScope | None = None,
) -> None:
super().__init__(level=level)
self._logger_provider = logger_provider or get_logger_provider()
self._instrumentation_scope = instrumentation_scope

warnings.warn(
"`LoggingHandler` in `opentelemetry-sdk` is deprecated. Use the "
Expand Down Expand Up @@ -638,7 +640,11 @@ def emit(self, record: logging.LogRecord) -> None:

The record is translated to OTel format, and then sent across the pipeline.
"""
logger = get_logger(record.name, logger_provider=self._logger_provider)
logger = get_logger(
record.name,
logger_provider=self._logger_provider,
instrumentation_scope=self._instrumentation_scope,
)
if not isinstance(logger, NoOpLogger):
logger.emit(self._translate(record))

Expand Down Expand Up @@ -856,6 +862,7 @@ def get_logger(
version: str | None = None,
schema_url: str | None = None,
attributes: _ExtendedAttributes | None = None,
instrumentation_scope: InstrumentationScope | None = None,
) -> APILogger:
if self._disabled:
return NoOpLogger(
Expand All @@ -864,13 +871,21 @@ def get_logger(
schema_url=schema_url,
attributes=attributes,
)
logger = (
self._get_logger_cached(name, version, schema_url)
if attributes is None
else self._get_logger_no_cache(
name, version, schema_url, attributes
if instrumentation_scope is None:
logger = (
self._get_logger_cached(name, version, schema_url)
if attributes is None
else self._get_logger_no_cache(
name, version, schema_url, attributes
)
)
else:
logger = self._get_logger_no_cache(
instrumentation_scope.name,
instrumentation_scope.version,
instrumentation_scope.schema_url,
instrumentation_scope.attributes,
)
)
with self._active_loggers_lock:
self._active_loggers.add(logger)
return logger
Expand Down
31 changes: 31 additions & 0 deletions opentelemetry-sdk/tests/logs/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,37 @@ def test_simple_log_record_processor_default_level(self):
finished_logs[0].instrumentation_scope.name, "default_level"
)

def test_simple_log_record_processor_instrumentation_scope(self):
exporter = InMemoryLogRecordExporter()
logger_provider = LoggerProvider()
instrumentation_scope = InstrumentationScope(
"custom_name",
"custom_version",
"custom_schema_url",
{"custom_key": "custom_value"},
)

logger_provider.add_log_record_processor(
SimpleLogRecordProcessor(exporter)
)

logger = logging.getLogger("custom_instrumentation_scope")
logger.propagate = False
logger.addHandler(
LoggingHandler(
logger_provider=logger_provider,
instrumentation_scope=instrumentation_scope,
)
)

logger.warning("Something is wrong")
finished_logs = exporter.get_finished_logs()

self.assertEqual(len(finished_logs), 1)
self.assertEqual(
finished_logs[0].instrumentation_scope, instrumentation_scope
)

def test_simple_log_record_processor_custom_level(self):
exporter = InMemoryLogRecordExporter()
logger_provider = LoggerProvider()
Expand Down
21 changes: 21 additions & 0 deletions opentelemetry-sdk/tests/logs/test_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,27 @@ def test_get_logger(self):
logger._instrumentation_scope.attributes, {"key": "value"}
)

def test_get_logger_with_instrumentation_scope(self):
"""
`LoggerProvider.get_logger` uses an explicitly provided
`InstrumentationScope` object on the created `Logger`.
"""
instrumentation_scope = InstrumentationScope(
"instrument_name",
"instrument_version",
"instrument_schema_url",
{"instrument_key": "instrument_value"},
)
logger = LoggerProvider().get_logger(
"name",
version="version",
schema_url="schema_url",
attributes={"key": "value"},
instrumentation_scope=instrumentation_scope,
)

self.assertEqual(logger._instrumentation_scope, instrumentation_scope)

@patch.dict("os.environ", {OTEL_SDK_DISABLED: "true"})
def test_get_logger_with_sdk_disabled(self):
logger = LoggerProvider().get_logger(Mock())
Expand Down
Loading