Skip to content

Commit 7557260

Browse files
authored
Add option to log payloads on error response from the trace agent
Adding new environment variable `_DD_TRACE_WRITER_LOG_ERROR_PAYLOADS` which when enabled will update the existing "failed to send traces to Datadog agent" log message to include the payload.
1 parent f65e636 commit 7557260

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

ddtrace/internal/writer.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import abc
2+
import binascii
23
from collections import defaultdict
34
from json import loads
45
import logging
@@ -22,6 +23,7 @@
2223
from ..constants import KEEP_SPANS_RATE_KEY
2324
from ..sampler import BasePrioritySampler
2425
from ..sampler import BaseSampler
26+
from ..utils.formats import asbool
2527
from ..utils.formats import get_env
2628
from ..utils.formats import parse_tags_str
2729
from ..utils.time import StopWatch
@@ -300,6 +302,7 @@ def __init__(
300302
stop=tenacity.stop_after_attempt(self.RETRY_ATTEMPTS),
301303
retry=tenacity.retry_if_exception_type((compat.httplib.HTTPException, OSError, IOError)),
302304
)
305+
self._log_error_payloads = asbool(os.environ.get("_DD_TRACE_WRITER_LOG_ERROR_PAYLOADS", False))
303306

304307
def _metrics_dist(self, name, count=1, tags=None):
305308
self._metrics[name]["count"] += count
@@ -407,12 +410,22 @@ def _send_payload(self, payload, count):
407410
if payload is not None:
408411
self._send_payload(payload, count)
409412
elif response.status >= 400:
410-
log.error(
411-
"failed to send traces to Datadog Agent at %s: HTTP error status %s, reason %s",
413+
msg = "failed to send traces to Datadog Agent at %s: HTTP error status %s, reason %s"
414+
log_args = (
412415
self.agent_url,
413416
response.status,
414417
response.reason,
415418
)
419+
# Append the payload if requested
420+
if self._log_error_payloads:
421+
msg += ", payload %s"
422+
# If the payload is bytes then hex encode the value before logging
423+
if isinstance(payload, six.binary_type):
424+
log_args += (binascii.hexlify(payload).decode(),)
425+
else:
426+
log_args += (payload,)
427+
428+
log.error(msg, *log_args)
416429
self._metrics_dist("http.dropped.bytes", len(payload))
417430
self._metrics_dist("http.dropped.traces", count)
418431
elif self._priority_sampler or isinstance(self._sampler, BasePrioritySampler):

tests/integration/test_integration.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,84 @@ def encode_traces(self, traces):
393393
log.error.assert_has_calls(calls)
394394

395395

396+
@pytest.mark.skipif(AGENT_VERSION == "testagent", reason="FIXME: Test agent response is different.")
397+
def test_bad_payload_log_payload(monkeypatch):
398+
monkeypatch.setenv("_DD_TRACE_WRITER_LOG_ERROR_PAYLOADS", "true")
399+
t = Tracer()
400+
401+
class BadEncoder:
402+
def __len__(self):
403+
return 0
404+
405+
def put(self, trace):
406+
pass
407+
408+
def encode(self):
409+
return b"bad_payload"
410+
411+
def encode_traces(self, traces):
412+
return b"bad_payload"
413+
414+
t.writer._encoder = BadEncoder()
415+
with mock.patch("ddtrace.internal.writer.log") as log:
416+
t.trace("asdf").finish()
417+
t.shutdown()
418+
calls = [
419+
mock.call(
420+
"failed to send traces to Datadog Agent at %s: HTTP error status %s, reason %s, payload %s",
421+
"http://localhost:8126",
422+
400,
423+
"Bad Request",
424+
"6261645f7061796c6f6164",
425+
)
426+
]
427+
log.error.assert_has_calls(calls)
428+
429+
430+
@pytest.mark.skipif(AGENT_VERSION == "testagent", reason="FIXME: Test agent response is different.")
431+
def test_bad_payload_log_payload_non_bytes(monkeypatch):
432+
monkeypatch.setenv("_DD_TRACE_WRITER_LOG_ERROR_PAYLOADS", "true")
433+
t = Tracer()
434+
435+
class BadEncoder:
436+
def __len__(self):
437+
return 0
438+
439+
def put(self, trace):
440+
pass
441+
442+
def encode(self):
443+
# Python 2.7
444+
# >>> isinstance("", bytes)
445+
# True
446+
# >>> isinstance(u"", bytes)
447+
# False
448+
# Python 3
449+
# >>> isinstance("", bytes)
450+
# False
451+
# >>> isinstance(u"", bytes)
452+
# False
453+
return u"bad_payload"
454+
455+
def encode_traces(self, traces):
456+
return u"bad_payload"
457+
458+
t.writer._encoder = BadEncoder()
459+
with mock.patch("ddtrace.internal.writer.log") as log:
460+
t.trace("asdf").finish()
461+
t.shutdown()
462+
calls = [
463+
mock.call(
464+
"failed to send traces to Datadog Agent at %s: HTTP error status %s, reason %s, payload %s",
465+
"http://localhost:8126",
466+
400,
467+
"Bad Request",
468+
"bad_payload",
469+
)
470+
]
471+
log.error.assert_has_calls(calls)
472+
473+
396474
def test_bad_encoder():
397475
t = Tracer()
398476

0 commit comments

Comments
 (0)