Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,17 @@ def _instrument(self, **kwargs):
config = structlog.get_config()
current_processors = list(config.get("processors", []))

# Insert before the last processor, assumed to be the renderer.
if current_processors:
insert_position = len(current_processors) - 1
else:
insert_position = 0
# Insert before the last processor, assumed to be the renderer, unless
# the app has already configured an OTel processor.
if not any(
isinstance(p, StructlogProcessor) for p in current_processors
):
if current_processors:
insert_position = len(current_processors) - 1
else:
insert_position = 0

current_processors.insert(insert_position, processor)
current_processors.insert(insert_position, processor)

# Reconfigure structlog with the new processor chain
StructlogInstrumentor._original_configure(
Expand Down Expand Up @@ -418,11 +422,11 @@ def _uninstrument(self, **kwargs):
config = structlog.get_config()
current_processors = list(config.get("processors", []))

# Remove all StructlogProcessor instances
# Remove only the processor added by this instrumentor.
new_processors = [
p
for p in current_processors
if not isinstance(p, StructlogProcessor)
if p is not StructlogInstrumentor._processor
]
configured_by_app = StructlogInstrumentor._is_configured_by_app

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,48 @@ def test_instrument_adds_processor(self):
)
self.assertTrue(has_otel_processor)

def test_instrument_does_not_duplicate_existing_processor(self):
"""Test instrument() reuses an existing OTel processor."""
existing_processor = StructlogProcessor(
logger_provider=self.logger_provider
)
structlog.configure(
processors=[
existing_processor,
structlog.dev.ConsoleRenderer(),
]
)

StructlogInstrumentor().instrument(
logger_provider=self.logger_provider
)

processors = structlog.get_config()["processors"]
otel_processors = [
p for p in processors if isinstance(p, StructlogProcessor)
]
self.assertEqual(otel_processors, [existing_processor])

def test_uninstrument_preserves_existing_processor(self):
"""Test uninstrument() preserves an app-configured OTel processor."""
existing_processor = StructlogProcessor(
logger_provider=self.logger_provider
)
structlog.configure(
processors=[
existing_processor,
structlog.dev.ConsoleRenderer(),
]
)

StructlogInstrumentor().instrument(
logger_provider=self.logger_provider
)
StructlogInstrumentor().uninstrument()

processors = structlog.get_config()["processors"]
self.assertIn(existing_processor, processors)

def test_instrument_without_prior_structlog_configuration_emits_logs(self):
"""Test instrument() emits logs before the app configures structlog."""
structlog.reset_defaults()
Expand Down
Loading