|
14 | 14 |
|
15 | 15 | # pylint: disable=protected-access |
16 | 16 |
|
| 17 | +import importlib |
| 18 | +import logging |
17 | 19 | import unittest |
18 | 20 | from unittest.mock import Mock, patch |
19 | 21 |
|
| 22 | +import opentelemetry.sdk._logs._internal as _logs_internal |
20 | 23 | from opentelemetry._logs import LogRecord, SeverityNumber |
21 | 24 | from opentelemetry.context import get_current |
22 | 25 | from opentelemetry.metrics import NoOpMeterProvider |
|
26 | 29 | ReadableLogRecord, |
27 | 30 | ) |
28 | 31 | from opentelemetry.sdk._logs._internal import ( |
| 32 | + _OTEL_LOG_LEVEL_TO_PYTHON, |
29 | 33 | LoggerMetrics, |
30 | 34 | NoOpLogger, |
31 | 35 | SynchronousMultiLogRecordProcessor, |
32 | 36 | _disable_logger_configurator, |
33 | 37 | _LoggerConfig, |
34 | 38 | _RuleBasedLoggerConfigurator, |
35 | 39 | ) |
36 | | -from opentelemetry.sdk.environment_variables import OTEL_SDK_DISABLED |
| 40 | +from opentelemetry.sdk.environment_variables import ( |
| 41 | + OTEL_LOG_LEVEL, |
| 42 | + OTEL_SDK_DISABLED, |
| 43 | +) |
37 | 44 | from opentelemetry.sdk.resources import Resource |
38 | 45 | from opentelemetry.sdk.util.instrumentation import ( |
39 | 46 | InstrumentationScope, |
@@ -354,3 +361,84 @@ def test_can_emit_with_keywords_arguments(self): |
354 | 361 | self.assertEqual(result_log_record.attributes, {"some": "attributes"}) |
355 | 362 | self.assertEqual(result_log_record.event_name, "event_name") |
356 | 363 | self.assertEqual(log_data.resource, logger.resource) |
| 364 | + |
| 365 | + |
| 366 | +class TestOtelLogLevelEnvVar(unittest.TestCase): |
| 367 | + """Tests for OTEL_LOG_LEVEL → SDK internal logger level.""" |
| 368 | + |
| 369 | + def setUp(self): |
| 370 | + self._sdk_logger = logging.getLogger("opentelemetry.sdk") |
| 371 | + |
| 372 | + def tearDown(self): |
| 373 | + importlib.reload(_logs_internal) |
| 374 | + |
| 375 | + def test_otel_log_level_to_python_mapping_accepted_values(self): |
| 376 | + expected_keys = { |
| 377 | + "debug", |
| 378 | + "info", |
| 379 | + "warn", |
| 380 | + "warning", |
| 381 | + "error", |
| 382 | + "critical", |
| 383 | + } |
| 384 | + self.assertEqual(set(_OTEL_LOG_LEVEL_TO_PYTHON.keys()), expected_keys) |
| 385 | + |
| 386 | + @patch.dict("os.environ", {OTEL_LOG_LEVEL: ""}) |
| 387 | + def test_default_level_is_info(self): |
| 388 | + importlib.reload(_logs_internal) |
| 389 | + self.assertEqual(self._sdk_logger.level, logging.INFO) |
| 390 | + |
| 391 | + def test_invalid_value_warns_and_defaults_to_info(self): |
| 392 | + # "trace", "verbose", "none" are valid in other SDKs but not accepted here |
| 393 | + for invalid in ("INVALID", "trace", "verbose", "none", "0"): |
| 394 | + with self.subTest(invalid=invalid): |
| 395 | + with patch.dict("os.environ", {OTEL_LOG_LEVEL: invalid}): |
| 396 | + with self.assertLogs( |
| 397 | + "opentelemetry.sdk._logs._internal", |
| 398 | + level=logging.WARNING, |
| 399 | + ): |
| 400 | + importlib.reload(_logs_internal) |
| 401 | + self.assertEqual(self._sdk_logger.level, logging.INFO) |
| 402 | + |
| 403 | + def test_case_insensitive(self): |
| 404 | + for env_value, expected_level in ( |
| 405 | + ("DEBUG", logging.DEBUG), |
| 406 | + ("WARN", logging.WARNING), |
| 407 | + ("Warning", logging.WARNING), |
| 408 | + ("cRiTiCaL", logging.CRITICAL), |
| 409 | + ): |
| 410 | + with self.subTest(env_value=env_value): |
| 411 | + with patch.dict("os.environ", {OTEL_LOG_LEVEL: env_value}): |
| 412 | + importlib.reload(_logs_internal) |
| 413 | + self.assertEqual(self._sdk_logger.level, expected_level) |
| 414 | + |
| 415 | + @patch.dict("os.environ", {OTEL_LOG_LEVEL: "critical"}) |
| 416 | + def test_level_propagates_to_child_loggers(self): |
| 417 | + importlib.reload(_logs_internal) |
| 418 | + self.assertEqual( |
| 419 | + self._sdk_logger.getChild("trace").getEffectiveLevel(), |
| 420 | + logging.CRITICAL, |
| 421 | + ) |
| 422 | + self.assertEqual( |
| 423 | + self._sdk_logger.getChild("metrics").getEffectiveLevel(), |
| 424 | + logging.CRITICAL, |
| 425 | + ) |
| 426 | + self.assertEqual( |
| 427 | + self._sdk_logger.getChild("logs").getEffectiveLevel(), |
| 428 | + logging.CRITICAL, |
| 429 | + ) |
| 430 | + |
| 431 | + def test_all_valid_values_map_to_correct_level(self): |
| 432 | + cases = [ |
| 433 | + ("debug", logging.DEBUG), |
| 434 | + ("info", logging.INFO), |
| 435 | + ("warn", logging.WARNING), |
| 436 | + ("warning", logging.WARNING), |
| 437 | + ("error", logging.ERROR), |
| 438 | + ("critical", logging.CRITICAL), |
| 439 | + ] |
| 440 | + for env_value, expected_level in cases: |
| 441 | + with self.subTest(env_value=env_value): |
| 442 | + with patch.dict("os.environ", {OTEL_LOG_LEVEL: env_value}): |
| 443 | + importlib.reload(_logs_internal) |
| 444 | + self.assertEqual(self._sdk_logger.level, expected_level) |
0 commit comments