|
7 | 7 |
|
8 | 8 | from aws_xray_sdk.core import xray_recorder |
9 | 9 |
|
| 10 | +from ddtrace import patch, tracer |
10 | 11 | from datadog_lambda.constants import ( |
11 | 12 | SamplingPriority, |
12 | 13 | TraceHeader, |
|
18 | 19 | dd_trace_context = {} |
19 | 20 |
|
20 | 21 |
|
| 22 | +def _convert_xray_trace_id(xray_trace_id): |
| 23 | + """ |
| 24 | + Convert X-Ray trace id (hex)'s last 63 bits to a Datadog trace id (int). |
| 25 | + """ |
| 26 | + return str(0x7FFFFFFFFFFFFFFF & int(xray_trace_id[-16:], 16)) |
| 27 | + |
| 28 | + |
| 29 | +def _convert_xray_entity_id(xray_entity_id): |
| 30 | + """ |
| 31 | + Convert X-Ray (sub)segement id (hex) to a Datadog span id (int). |
| 32 | + """ |
| 33 | + return str(int(xray_entity_id, 16)) |
| 34 | + |
| 35 | + |
| 36 | +def _convert_xray_sampling(xray_sampled): |
| 37 | + """ |
| 38 | + Convert X-Ray sampled (True/False) to its Datadog counterpart. |
| 39 | + """ |
| 40 | + return str(SamplingPriority.USER_KEEP) if xray_sampled \ |
| 41 | + else str(SamplingPriority.USER_REJECT) |
| 42 | + |
| 43 | + |
21 | 44 | def extract_dd_trace_context(event): |
22 | 45 | """ |
23 | 46 | Extract Datadog trace context from the Lambda `event` object. |
@@ -56,6 +79,8 @@ def extract_dd_trace_context(event): |
56 | 79 | # reset to avoid using the context from the last invocation. |
57 | 80 | dd_trace_context = {} |
58 | 81 |
|
| 82 | + logger.debug('extracted dd trace context %s', dd_trace_context) |
| 83 | + |
59 | 84 |
|
60 | 85 | def get_dd_trace_context(): |
61 | 86 | """ |
@@ -91,23 +116,41 @@ def get_dd_trace_context(): |
91 | 116 | } |
92 | 117 |
|
93 | 118 |
|
94 | | -def _convert_xray_trace_id(xray_trace_id): |
| 119 | +def set_correlation_ids(): |
95 | 120 | """ |
96 | | - Convert X-Ray trace id (hex)'s last 63 bits to a Datadog trace id (int). |
97 | | - """ |
98 | | - return str(0x7FFFFFFFFFFFFFFF & int(xray_trace_id[-16:], 16)) |
| 121 | + Create a dummy span, and overrides its trace_id and span_id, to make |
| 122 | + ddtrace.helpers.get_correlation_ids() return the correct ids for both |
| 123 | + auto and manual log correlations. |
99 | 124 |
|
100 | | - |
101 | | -def _convert_xray_entity_id(xray_entity_id): |
| 125 | + TODO: Remove me when Datadog tracer is natively supported in Lambda. |
102 | 126 | """ |
103 | | - Convert X-Ray (sub)segement id (hex) to a Datadog span id (int). |
104 | | - """ |
105 | | - return str(int(xray_entity_id, 16)) |
| 127 | + context = get_dd_trace_context() |
| 128 | + span = tracer.trace('dummy.span') |
| 129 | + span.trace_id = context[TraceHeader.TRACE_ID] |
| 130 | + span.span_id = context[TraceHeader.PARENT_ID] |
106 | 131 |
|
| 132 | + logger.debug('correlation ids set') |
107 | 133 |
|
108 | | -def _convert_xray_sampling(xray_sampled): |
| 134 | + |
| 135 | +def inject_correlation_ids(): |
109 | 136 | """ |
110 | | - Convert X-Ray sampled (True/False) to its Datadog counterpart. |
| 137 | + Override the formatter of LambdaLoggerHandler to inject datadog trace and |
| 138 | + span id for log correlation. |
| 139 | +
|
| 140 | + For manual injections to custom log handlers, use `ddtrace.helpers.get_correlation_ids` |
| 141 | + to retrieve correlation ids (trace_id, span_id). |
111 | 142 | """ |
112 | | - return str(SamplingPriority.USER_KEEP) if xray_sampled \ |
113 | | - else str(SamplingPriority.USER_REJECT) |
| 143 | + # Override the log format of the AWS provided LambdaLoggerHandler |
| 144 | + root_logger = logging.getLogger() |
| 145 | + for handler in root_logger.handlers: |
| 146 | + if handler.__class__.__name__ == 'LambdaLoggerHandler': |
| 147 | + handler.setFormatter(logging.Formatter( |
| 148 | + '[%(levelname)s]\t%(asctime)s.%(msecs)dZ\t%(aws_request_id)s\t' |
| 149 | + '[dd.trace_id=%(dd.trace_id)s dd.span_id=%(dd.span_id)s]\t%(message)s\n', |
| 150 | + '%Y-%m-%dT%H:%M:%S' |
| 151 | + )) |
| 152 | + |
| 153 | + # Patch `logging.Logger.makeRecord` to actually inject correlation ids |
| 154 | + patch(logging=True) |
| 155 | + |
| 156 | + logger.debug('logs injection configured') |
0 commit comments