From c6c0db1d8f375059531b86ecde09a232fce1834d Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Tue, 6 Jan 2026 14:41:34 +0000 Subject: [PATCH 1/5] Siwtch recursion suppression to use ContextVar --- .../src/opentelemetry/context/__init__.py | 1 + .../sdk/_logs/_internal/export/__init__.py | 29 +++++++------------ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index cad7f951428..39772554d5a 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -160,6 +160,7 @@ def detach(token: Token[Context]) -> None: # FIXME This is a temporary location for the suppress instrumentation key. # Once the decision around how to suppress instrumentation is made in the # spec, this key should be moved accordingly. +_ON_EMIT_RECURSION_COUNT_KEY = create_key("on_emit_recursion_count") _SUPPRESS_INSTRUMENTATION_KEY = create_key("suppress_instrumentation") _SUPPRESS_HTTP_INSTRUMENTATION_KEY = create_key( "suppress_http_instrumentation" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index ba0861c2a03..b8a90b23149 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -17,16 +17,17 @@ import enum import logging import sys -import traceback from os import environ, linesep from typing import IO, Callable, Optional, Sequence from typing_extensions import deprecated from opentelemetry.context import ( + _ON_EMIT_RECURSION_COUNT_KEY, _SUPPRESS_INSTRUMENTATION_KEY, attach, detach, + get_value, set_value, ) from opentelemetry.sdk._logs import ( @@ -150,28 +151,18 @@ def __init__(self, exporter: LogRecordExporter): def on_emit(self, log_record: ReadWriteLogRecord): # Prevent entering a recursive loop. - if ( - sum( - item.name == "on_emit" - and ( - item.filename.endswith("export/__init__.py") - or item.filename.endswith( - r"export\__init__.py" - ) # backward slash on windows.. - ) - for item in traceback.extract_stack() - ) - # Recursive depth of 3 is sort of arbitrary. It's possible that an Exporter.export call - # emits a log which returns us to this function, but when we call Exporter.export again the log - # is no longer emitted and we exit this recursive loop naturally, a depth of >3 allows 3 - # recursive log calls but exits after because it's likely endless. - > 3 - ): + cnt = get_value(_ON_EMIT_RECURSION_COUNT_KEY) or 0 + # Recursive depth of 3 is sort of arbitrary. It's possible that an Exporter.export call + # emits a log which returns us to this function, but when we call Exporter.export again the log + # is no longer emitted and we exit this recursive loop naturally, a depth of >3 allows 3 + # recursive log calls but exits after because it's likely endless. + if cnt > 3: _propagate_false_logger.warning( "SimpleLogRecordProcessor.on_emit has entered a recursive loop. Dropping log and exiting the loop." ) return - token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + set_value(_SUPPRESS_INSTRUMENTATION_KEY, True) + token = attach(set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1)) try: if self._shutdown: _logger.warning("Processor is already shutdown, ignoring call") From f1031f191cb05527dfb4281411777488813cc43d Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Tue, 6 Jan 2026 14:56:34 +0000 Subject: [PATCH 2/5] Add changelog fix typecheck --- CHANGELOG.md | 2 +- .../opentelemetry/sdk/_logs/_internal/export/__init__.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c37861aa3b5..884ecab8a36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Silence events API warnings for internal users ([#4847](https://github.com/open-telemetry/opentelemetry-python/pull/4847)) - Prevent possible endless recursion from happening in `SimpleLogRecordProcessor.on_emit`, - ([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)). + ([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)) and ([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)). ## Version 1.39.0/0.60b0 (2025-12-03) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index b8a90b23149..de6f25fb825 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -156,13 +156,13 @@ def on_emit(self, log_record: ReadWriteLogRecord): # emits a log which returns us to this function, but when we call Exporter.export again the log # is no longer emitted and we exit this recursive loop naturally, a depth of >3 allows 3 # recursive log calls but exits after because it's likely endless. - if cnt > 3: + if cnt > 3: # pyright: ignore[reportOperatorIssue] _propagate_false_logger.warning( "SimpleLogRecordProcessor.on_emit has entered a recursive loop. Dropping log and exiting the loop." ) return - set_value(_SUPPRESS_INSTRUMENTATION_KEY, True) - token = attach(set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1)) + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + attach(set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1)) # pyright: ignore[reportOperatorIssue] try: if self._shutdown: _logger.warning("Processor is already shutdown, ignoring call") From 0fcdb1cdd197578ffff69aaf868c9119337fe5d0 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Tue, 6 Jan 2026 15:00:08 +0000 Subject: [PATCH 3/5] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 884ecab8a36..d5f6c7aea88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Silence events API warnings for internal users ([#4847](https://github.com/open-telemetry/opentelemetry-python/pull/4847)) - Prevent possible endless recursion from happening in `SimpleLogRecordProcessor.on_emit`, - ([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)) and ([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)). + ([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)) and ([#4867](https://github.com/open-telemetry/opentelemetry-python/pull/4867)). ## Version 1.39.0/0.60b0 (2025-12-03) From b374695b290704802353b2344993c9f4891227ec Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 8 Jan 2026 14:18:57 +0000 Subject: [PATCH 4/5] minor change --- .../sdk/_logs/_internal/export/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index de6f25fb825..d36df8cb98c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -161,8 +161,14 @@ def on_emit(self, log_record: ReadWriteLogRecord): "SimpleLogRecordProcessor.on_emit has entered a recursive loop. Dropping log and exiting the loop." ) return - token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) - attach(set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1)) # pyright: ignore[reportOperatorIssue] + token = attach( + set_value( + _SUPPRESS_INSTRUMENTATION_KEY, + True, + set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1), + ) + ) + # pyright: ignore[reportOperatorIssue] try: if self._shutdown: _logger.warning("Processor is already shutdown, ignoring call") From 5653acacf57b306960e7c54357440b5bb57a73f3 Mon Sep 17 00:00:00 2001 From: Dylan Russell Date: Thu, 8 Jan 2026 17:05:47 +0000 Subject: [PATCH 5/5] Fix typecheck --- .../src/opentelemetry/sdk/_logs/_internal/export/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index d36df8cb98c..f12b9dd8a2d 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -165,10 +165,9 @@ def on_emit(self, log_record: ReadWriteLogRecord): set_value( _SUPPRESS_INSTRUMENTATION_KEY, True, - set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1), + set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1), # pyright: ignore[reportOperatorIssue] ) ) - # pyright: ignore[reportOperatorIssue] try: if self._shutdown: _logger.warning("Processor is already shutdown, ignoring call")