Skip to content

Commit 656fba2

Browse files
committed
Fix max recursion bug by removing logging.log calls in emit
1 parent 3462204 commit 656fba2

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

opentelemetry-sdk/src/opentelemetry/sdk/_shared_internal/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,14 @@ def _export(self, batch_strategy: BatchExportStrategy) -> None:
167167
)
168168
detach(token)
169169

170+
# Do not add any logging.log statements to this function, they can be being routed back to this `emit` function,
171+
# resulting in endless recursive calls that crash the program.
170172
def emit(self, data: Telemetry) -> None:
171173
if self._shutdown:
172-
self._logger.info("Shutdown called, ignoring %s.", self._exporting)
173174
return
174175
if self._pid != os.getpid():
175176
self._bsp_reset_once.do_once(self._at_fork_reinit)
176-
177-
if len(self._queue) == self._max_queue_size:
178-
self._logger.warning("Queue full, dropping %s.", self._exporting)
177+
# This will drop a log from the right side if the queue is at _max_queue_length.
179178
self._queue.appendleft(data)
180179
if len(self._queue) >= self._max_export_batch_size:
181180
self._worker_awaken.set()

opentelemetry-sdk/tests/shared_internal/test_batch_processor.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
# pylint: disable=protected-access
1616
import gc
17+
import logging
1718
import multiprocessing
1819
import os
1920
import time
@@ -27,13 +28,17 @@
2728
import pytest
2829
from pytest import mark
2930

31+
from opentelemetry._logs import set_logger_provider
3032
from opentelemetry.sdk._logs import (
3133
LogData,
34+
LoggerProvider,
35+
LoggingHandler,
3236
LogRecord,
3337
)
3438
from opentelemetry.sdk._logs.export import (
3539
BatchLogRecordProcessor,
3640
)
41+
from opentelemetry.sdk.resources import Resource
3742
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
3843

3944
EMPTY_LOG = LogData(
@@ -88,7 +93,7 @@ def test_telemetry_exported_once_schedule_delay_reached(
8893
exporter.export.assert_called_once_with([telemetry])
8994

9095
def test_telemetry_flushed_before_shutdown_and_dropped_after_shutdown(
91-
self, batch_processor_class, telemetry, caplog
96+
self, batch_processor_class, telemetry
9297
):
9398
exporter = Mock()
9499
batch_processor = batch_processor_class(
@@ -107,8 +112,6 @@ def test_telemetry_flushed_before_shutdown_and_dropped_after_shutdown(
107112

108113
# This should not be flushed.
109114
batch_processor.emit(telemetry)
110-
assert len(caplog.records) == 1
111-
assert "Shutdown called, ignoring" in caplog.text
112115
exporter.export.assert_called_once()
113116

114117
# pylint: disable=no-self-use
@@ -211,3 +214,29 @@ def test_record_processor_is_garbage_collected(
211214

212215
# Then the reference to the processor should no longer exist
213216
assert weak_ref() is None
217+
218+
def test_logging_lib_not_invoked_in_emit(
219+
self, batch_processor_class, telemetry
220+
):
221+
exporter = Mock()
222+
processor = batch_processor_class(exporter)
223+
processor._batch_processor.emit(telemetry)
224+
logger_provider = LoggerProvider(
225+
resource=Resource.create(
226+
{
227+
"service.name": "shoppingcart",
228+
"service.instance.id": "instance-12",
229+
}
230+
),
231+
)
232+
set_logger_provider(logger_provider)
233+
logger_provider.add_log_record_processor(processor)
234+
handler = LoggingHandler(
235+
level=logging.INFO, logger_provider=logger_provider
236+
)
237+
# Attach OTLP handler to root logger
238+
logging.getLogger().addHandler(handler)
239+
# If `emit` calls logging.log then this test will throw a maximum recursion depth exceeded exception and fail.
240+
processor.emit(telemetry)
241+
processor.shutdown()
242+
processor.emit(telemetry)

0 commit comments

Comments
 (0)