Skip to content

Commit 1bcf276

Browse files
github-actions[bot]zarirhamzamabdinur
authored
fix(structlog): appends to default_processors via list to avoid TypeError [backport 2.2] (#7691)
Backport 0cfc263 from #7679 to 2.2. Fixes #7665 by changing `default_processors` to list accounting for all types of iterables before appending ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [x] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) - [x] If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. - [x] This PR doesn't touch any of that. Co-authored-by: Zarir Hamza <[email protected]> Co-authored-by: Munir Abdinur <[email protected]>
1 parent 98cc900 commit 1bcf276

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

ddtrace/contrib/structlog/patch.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,18 @@ def _tracer_injection(_, __, event_dict):
4848

4949

5050
def _w_get_logger(func, instance, args, kwargs):
51+
"""
52+
Append the tracer injection processor to the ``default_processors`` list used by the logger
53+
The ``default_processors`` list has built in defaults which protects against a user configured ``None`` value.
54+
The argument to configure ``default_processors`` accepts an iterable type:
55+
- List: default use case which has been accounted for
56+
- Tuple: patched via list conversion
57+
- Set: ignored because structlog processors care about order notably the last value to be a Renderer
58+
- Dict: because keys are ignored, this essentially becomes a List
59+
"""
60+
5161
dd_processor = [_tracer_injection]
52-
structlog._config._CONFIG.default_processors = dd_processor + structlog._config._CONFIG.default_processors
62+
structlog._config._CONFIG.default_processors = dd_processor + list(structlog._config._CONFIG.default_processors)
5363
return func(*args, **kwargs)
5464

5565

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
structlog: Fixes ``TypeError`` raised when ddtrace log processor is configured with a tuple

tests/contrib/structlog/test_structlog_logging.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,88 @@ def test_log_DD_TAGS():
259259

260260
cf.logger.calls.clear()
261261
unpatch()
262+
263+
264+
@pytest.mark.subprocess()
265+
def test_tuple_processor_list():
266+
"""
267+
Regression test for: https://github.com/DataDog/dd-trace-py/issues/7665
268+
"""
269+
import json
270+
271+
import structlog
272+
273+
from ddtrace import config
274+
from ddtrace import tracer
275+
from ddtrace.contrib.structlog import patch
276+
from ddtrace.contrib.structlog import unpatch
277+
278+
config.service = "logging"
279+
config.env = "global.env"
280+
config.version = "global.version"
281+
282+
patch()
283+
284+
cf = structlog.testing.CapturingLoggerFactory()
285+
structlog.configure(
286+
processors=(structlog.stdlib.add_log_level, structlog.processors.JSONRenderer()),
287+
logger_factory=cf,
288+
)
289+
logger = structlog.getLogger()
290+
291+
span = tracer.trace("test.logging")
292+
logger.info("Hello!")
293+
span.finish()
294+
295+
output = cf.logger.calls
296+
297+
assert json.loads(output[0].args[0])["event"] == "Hello!"
298+
assert json.loads(output[0].args[0])["dd.trace_id"] == str(span._trace_id_64bits)
299+
assert json.loads(output[0].args[0])["dd.span_id"] == str(span.span_id)
300+
assert json.loads(output[0].args[0])["dd.env"] == "global.env"
301+
assert json.loads(output[0].args[0])["dd.service"] == "logging"
302+
assert json.loads(output[0].args[0])["dd.version"] == "global.version"
303+
304+
cf.logger.calls.clear()
305+
unpatch()
306+
307+
308+
@pytest.mark.subprocess()
309+
def test_no_configured_processor():
310+
"""
311+
Check if injected values are present when no processor is configured
312+
"""
313+
import structlog
314+
315+
from ddtrace import config
316+
from ddtrace import tracer
317+
from ddtrace.contrib.structlog import patch
318+
from ddtrace.contrib.structlog import unpatch
319+
320+
config.service = "logging"
321+
config.env = "global.env"
322+
config.version = "global.version"
323+
324+
patch()
325+
326+
cf = structlog.testing.CapturingLoggerFactory()
327+
structlog.configure(
328+
logger_factory=cf,
329+
)
330+
logger = structlog.getLogger()
331+
332+
span = tracer.trace("test.logging")
333+
logger.info("Hello!")
334+
span.finish()
335+
336+
output = cf.logger.calls
337+
338+
assert "Hello!" in output[0].args[0]
339+
assert "dd.trace_id={}".format(str(span._trace_id_64bits)) in output[0].args[0]
340+
assert "dd.span_id={}".format(str(span.span_id)) in output[0].args[0]
341+
assert "dd.env=global.env" in output[0].args[0]
342+
assert "dd.service=logging" in output[0].args[0]
343+
assert "dd.version=global.version" in output[0].args[0]
344+
345+
cf.logger.calls.clear()
346+
unpatch()

0 commit comments

Comments
 (0)