Skip to content

Commit 3987f3b

Browse files
authored
Switch to using contextvar for recursion indicator (#4867)
* Siwtch recursion suppression to use ContextVar * Add changelog fix typecheck * Update changelog * minor change * Fix typecheck
1 parent 3750c14 commit 3987f3b

File tree

3 files changed

+17
-20
lines changed

3 files changed

+17
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- Silence events API warnings for internal users
2424
([#4847](https://github.com/open-telemetry/opentelemetry-python/pull/4847))
2525
- Prevent possible endless recursion from happening in `SimpleLogRecordProcessor.on_emit`,
26-
([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)).
26+
([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)) and ([#4867](https://github.com/open-telemetry/opentelemetry-python/pull/4867)).
2727

2828
## Version 1.39.0/0.60b0 (2025-12-03)
2929

opentelemetry-api/src/opentelemetry/context/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def detach(token: Token[Context]) -> None:
160160
# FIXME This is a temporary location for the suppress instrumentation key.
161161
# Once the decision around how to suppress instrumentation is made in the
162162
# spec, this key should be moved accordingly.
163+
_ON_EMIT_RECURSION_COUNT_KEY = create_key("on_emit_recursion_count")
163164
_SUPPRESS_INSTRUMENTATION_KEY = create_key("suppress_instrumentation")
164165
_SUPPRESS_HTTP_INSTRUMENTATION_KEY = create_key(
165166
"suppress_http_instrumentation"

opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@
1717
import enum
1818
import logging
1919
import sys
20-
import traceback
2120
from os import environ, linesep
2221
from typing import IO, Callable, Optional, Sequence
2322

2423
from typing_extensions import deprecated
2524

2625
from opentelemetry.context import (
26+
_ON_EMIT_RECURSION_COUNT_KEY,
2727
_SUPPRESS_INSTRUMENTATION_KEY,
2828
attach,
2929
detach,
30+
get_value,
3031
set_value,
3132
)
3233
from opentelemetry.sdk._logs import (
@@ -150,28 +151,23 @@ def __init__(self, exporter: LogRecordExporter):
150151

151152
def on_emit(self, log_record: ReadWriteLogRecord):
152153
# Prevent entering a recursive loop.
153-
if (
154-
sum(
155-
item.name == "on_emit"
156-
and (
157-
item.filename.endswith("export/__init__.py")
158-
or item.filename.endswith(
159-
r"export\__init__.py"
160-
) # backward slash on windows..
161-
)
162-
for item in traceback.extract_stack()
163-
)
164-
# Recursive depth of 3 is sort of arbitrary. It's possible that an Exporter.export call
165-
# emits a log which returns us to this function, but when we call Exporter.export again the log
166-
# is no longer emitted and we exit this recursive loop naturally, a depth of >3 allows 3
167-
# recursive log calls but exits after because it's likely endless.
168-
> 3
169-
):
154+
cnt = get_value(_ON_EMIT_RECURSION_COUNT_KEY) or 0
155+
# Recursive depth of 3 is sort of arbitrary. It's possible that an Exporter.export call
156+
# emits a log which returns us to this function, but when we call Exporter.export again the log
157+
# is no longer emitted and we exit this recursive loop naturally, a depth of >3 allows 3
158+
# recursive log calls but exits after because it's likely endless.
159+
if cnt > 3: # pyright: ignore[reportOperatorIssue]
170160
_propagate_false_logger.warning(
171161
"SimpleLogRecordProcessor.on_emit has entered a recursive loop. Dropping log and exiting the loop."
172162
)
173163
return
174-
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
164+
token = attach(
165+
set_value(
166+
_SUPPRESS_INSTRUMENTATION_KEY,
167+
True,
168+
set_value(_ON_EMIT_RECURSION_COUNT_KEY, cnt + 1), # pyright: ignore[reportOperatorIssue]
169+
)
170+
)
175171
try:
176172
if self._shutdown:
177173
_logger.warning("Processor is already shutdown, ignoring call")

0 commit comments

Comments
 (0)