Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4494](https://github.com/open-telemetry/opentelemetry-python/pull/4494))
- Improve CI by cancelling stale runs and setting timeouts
([#4498](https://github.com/open-telemetry/opentelemetry-python/pull/4498))
- Patch logging.basicConfig so OTel logs don't cause console logs to disappear
([#4436](https://github.com/open-telemetry/opentelemetry-python/pull/4436))
- Fix ExplicitBucketHistogramAggregation to handle multiple explicit bucket boundaries advisories
([#4521](https://github.com/open-telemetry/opentelemetry-python/pull/4521))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,33 @@ def _init_logging(
set_event_logger_provider(event_logger_provider)

if setup_logging_handler:
_patch_basic_config()

# Add OTel handler
handler = LoggingHandler(
level=logging.NOTSET, logger_provider=provider
)
logging.getLogger().addHandler(handler)


def _patch_basic_config():
original_basic_config = logging.basicConfig

def patched_basic_config(*args, **kwargs):
root = logging.getLogger()
has_only_otel = len(root.handlers) == 1 and isinstance(
root.handlers[0], LoggingHandler
)
if has_only_otel:
otel_handler = root.handlers.pop()
original_basic_config(*args, **kwargs)
root.addHandler(otel_handler)
else:
original_basic_config(*args, **kwargs)

logging.basicConfig = patched_basic_config


def _import_exporters(
trace_exporter_names: Sequence[str],
metric_exporter_names: Sequence[str],
Expand Down
95 changes: 94 additions & 1 deletion opentelemetry-sdk/tests/test_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# pylint: skip-file
from __future__ import annotations

import logging
from logging import WARNING, getLogger
from os import environ
from typing import Iterable, Optional, Sequence
Expand Down Expand Up @@ -44,6 +45,7 @@
_OTelSDKConfigurator,
)
from opentelemetry.sdk._logs import LoggingHandler
from opentelemetry.sdk._logs._internal.export import LogExporter
from opentelemetry.sdk._logs.export import ConsoleLogExporter
from opentelemetry.sdk.environment_variables import (
OTEL_TRACES_SAMPLER,
Expand Down Expand Up @@ -203,7 +205,7 @@ class OTLPSpanExporter:
pass


class DummyOTLPLogExporter:
class DummyOTLPLogExporter(LogExporter):
def __init__(self, *args, **kwargs):
self.export_called = False

Expand Down Expand Up @@ -841,6 +843,60 @@ def test_initialize_components_kwargs(
True,
)

def test_basicConfig_works_with_otel_handler(self):
with ClearLoggingHandlers():
_init_logging(
{"otlp": DummyOTLPLogExporter},
Resource.create({}),
setup_logging_handler=True,
)

logging.basicConfig(level=logging.INFO)

root_logger = logging.getLogger()
stream_handlers = [
h
for h in root_logger.handlers
if isinstance(h, logging.StreamHandler)
]
self.assertEqual(
len(stream_handlers),
1,
"basicConfig should add a StreamHandler even when OTel handler exists",
)

def test_basicConfig_preserves_otel_handler(self):
with ClearLoggingHandlers():
_init_logging(
{"otlp": DummyOTLPLogExporter},
Resource.create({}),
setup_logging_handler=True,
)

root_logger = logging.getLogger()
self.assertEqual(
len(root_logger.handlers),
1,
"Should be exactly one OpenTelemetry LoggingHandler",
)
handler = root_logger.handlers[0]
self.assertIsInstance(handler, LoggingHandler)

logging.basicConfig()

self.assertGreater(len(root_logger.handlers), 1)

logging_handlers = [
h
for h in root_logger.handlers
if isinstance(h, LoggingHandler)
]
self.assertEqual(
len(logging_handlers),
1,
"Should still have exactly one OpenTelemetry LoggingHandler",
)


class TestMetricsInit(TestCase):
def setUp(self):
Expand Down Expand Up @@ -1076,3 +1132,40 @@ def test_custom_configurator(self, mock_init_comp):
"sampler": "TEST_SAMPLER",
}
mock_init_comp.assert_called_once_with(**kwargs)


class ClearLoggingHandlers:
def __init__(self):
self.root_logger = getLogger()
self.original_handlers = None

def __enter__(self):
self.original_handlers = self.root_logger.handlers[:]
self.root_logger.handlers = []
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.root_logger.handlers = []
for handler in self.original_handlers:
self.root_logger.addHandler(handler)


class TestClearLoggingHandlers(TestCase):
def test_preserves_handlers(self):
root_logger = getLogger()
initial_handlers = root_logger.handlers[:]

test_handler = logging.StreamHandler()
root_logger.addHandler(test_handler)
expected_handlers = initial_handlers + [test_handler]

with ClearLoggingHandlers():
self.assertEqual(len(root_logger.handlers), 0)
temp_handler = logging.StreamHandler()
root_logger.addHandler(temp_handler)

self.assertEqual(len(root_logger.handlers), len(expected_handlers))
for h1, h2 in zip(root_logger.handlers, expected_handlers):
self.assertIs(h1, h2)

root_logger.removeHandler(test_handler)