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/5351.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-sdk`: wire the top-level `log_level` field in declarative configuration — when set, maps the OTel `SeverityNumber` value to a Python logging level and applies it to the `opentelemetry` root logger so SDK internal diagnostics respect the configured severity.
39 changes: 38 additions & 1 deletion opentelemetry-sdk/src/opentelemetry/sdk/_configuration/_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,43 @@
from opentelemetry.sdk._configuration._tracer_provider import (
configure_tracer_provider,
)
from opentelemetry.sdk._configuration.models import OpenTelemetryConfiguration
from opentelemetry.sdk._configuration.models import (
OpenTelemetryConfiguration,
SeverityNumber,
)

_logger = logging.getLogger(__name__)

# Maps OTel SeverityNumber groups to Python logging levels.
# The numbered variants (debug2, info3, …) are sub-levels within the same
# Python tier, so they collapse to the same level constant.
_SEVERITY_TO_LOGGING_LEVEL: dict[SeverityNumber, int] = {
SeverityNumber.trace: logging.DEBUG,
SeverityNumber.trace2: logging.DEBUG,
SeverityNumber.trace3: logging.DEBUG,
SeverityNumber.trace4: logging.DEBUG,
SeverityNumber.debug: logging.DEBUG,
SeverityNumber.debug2: logging.DEBUG,
SeverityNumber.debug3: logging.DEBUG,
SeverityNumber.debug4: logging.DEBUG,
SeverityNumber.info: logging.INFO,
SeverityNumber.info2: logging.INFO,
SeverityNumber.info3: logging.INFO,
SeverityNumber.info4: logging.INFO,
SeverityNumber.warn: logging.WARNING,
SeverityNumber.warn2: logging.WARNING,
SeverityNumber.warn3: logging.WARNING,
SeverityNumber.warn4: logging.WARNING,
SeverityNumber.error: logging.ERROR,
SeverityNumber.error2: logging.ERROR,
SeverityNumber.error3: logging.ERROR,
SeverityNumber.error4: logging.ERROR,
SeverityNumber.fatal: logging.CRITICAL,
SeverityNumber.fatal2: logging.CRITICAL,
SeverityNumber.fatal3: logging.CRITICAL,
SeverityNumber.fatal4: logging.CRITICAL,
}


def configure_sdk(config: OpenTelemetryConfiguration) -> None:
"""Configure the global SDK from a parsed declarative configuration.
Expand Down Expand Up @@ -57,6 +90,10 @@ def configure_sdk(config: OpenTelemetryConfiguration) -> None:
)
return

if config.log_level is not None:
level = _SEVERITY_TO_LOGGING_LEVEL.get(config.log_level, logging.INFO)
logging.getLogger("opentelemetry").setLevel(level)
Comment on lines +93 to +95

resource = create_resource(config.resource)
configure_tracer_provider(config.tracer_provider, resource)
configure_meter_provider(config.meter_provider, resource)
Expand Down
62 changes: 62 additions & 0 deletions opentelemetry-sdk/tests/_configuration/test_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
# Tests access private members of SDK classes to assert correct configuration.
# pylint: disable=protected-access

import logging
import unittest
from unittest.mock import patch

from opentelemetry.sdk._configuration._sdk import configure_sdk
from opentelemetry.sdk._configuration.models import (
OpenTelemetryConfiguration,
SeverityNumber,
)
from opentelemetry.sdk._configuration.models import (
Propagator as PropagatorConfig,
Expand Down Expand Up @@ -122,6 +124,66 @@ def test_absent_sections_pass_none(
self.assertEqual(mock_propagator.call_args.args[0], None)


class TestConfigureSdkLogLevel(unittest.TestCase):
def setUp(self):
# Reset the opentelemetry logger level before each test.
logging.getLogger("opentelemetry").setLevel(logging.NOTSET)

def tearDown(self):
logging.getLogger("opentelemetry").setLevel(logging.NOTSET)
Comment on lines +128 to +133

@patch("opentelemetry.sdk._configuration._sdk.configure_propagator")
@patch("opentelemetry.sdk._configuration._sdk.configure_logger_provider")
@patch("opentelemetry.sdk._configuration._sdk.configure_meter_provider")
@patch("opentelemetry.sdk._configuration._sdk.configure_tracer_provider")
@patch("opentelemetry.sdk._configuration._sdk.create_resource")
def test_sets_opentelemetry_logger_level(self, *_mocks):
configure_sdk(_config(log_level=SeverityNumber.warn))
self.assertEqual(
logging.getLogger("opentelemetry").level, logging.WARNING
)

@patch("opentelemetry.sdk._configuration._sdk.configure_propagator")
@patch("opentelemetry.sdk._configuration._sdk.configure_logger_provider")
@patch("opentelemetry.sdk._configuration._sdk.configure_meter_provider")
@patch("opentelemetry.sdk._configuration._sdk.configure_tracer_provider")
@patch("opentelemetry.sdk._configuration._sdk.create_resource")
def test_absent_log_level_leaves_logger_unchanged(self, *_mocks):
logging.getLogger("opentelemetry").setLevel(logging.ERROR)
configure_sdk(_config())
self.assertEqual(
logging.getLogger("opentelemetry").level, logging.ERROR
)

@patch("opentelemetry.sdk._configuration._sdk.configure_propagator")
@patch("opentelemetry.sdk._configuration._sdk.configure_logger_provider")
@patch("opentelemetry.sdk._configuration._sdk.configure_meter_provider")
@patch("opentelemetry.sdk._configuration._sdk.configure_tracer_provider")
@patch("opentelemetry.sdk._configuration._sdk.create_resource")
def test_severity_number_variants_map_correctly(self, *_mocks):
cases = [
(SeverityNumber.trace, logging.DEBUG),
(SeverityNumber.trace4, logging.DEBUG),
(SeverityNumber.debug, logging.DEBUG),
(SeverityNumber.debug4, logging.DEBUG),
(SeverityNumber.info, logging.INFO),
(SeverityNumber.info4, logging.INFO),
(SeverityNumber.warn, logging.WARNING),
(SeverityNumber.warn4, logging.WARNING),
(SeverityNumber.error, logging.ERROR),
(SeverityNumber.error4, logging.ERROR),
(SeverityNumber.fatal, logging.CRITICAL),
(SeverityNumber.fatal4, logging.CRITICAL),
]
Comment on lines +164 to +177
for severity, expected_level in cases:
with self.subTest(severity=severity):
configure_sdk(_config(log_level=severity))
self.assertEqual(
logging.getLogger("opentelemetry").level,
expected_level,
)


class TestConfigureSdkIntegration(unittest.TestCase):
"""End-to-end: build a real OpenTelemetryConfiguration and apply it."""

Expand Down
Loading