Skip to content

Commit 79668df

Browse files
committed
add config.decodeAuthorizerContext and httpapi no zero-length span
1 parent 73a9132 commit 79668df

File tree

7 files changed

+91
-82
lines changed

7 files changed

+91
-82
lines changed

src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const mergeXrayTracesEnvVar = "DD_MERGE_XRAY_TRACES";
3535
export const traceExtractorEnvVar = "DD_TRACE_EXTRACTOR";
3636
export const defaultSiteURL = "datadoghq.com";
3737
export const encodeAuthorizerContextEnvVar = "DD_ENCODE_AUTHORIZER_CONTEXT";
38+
export const decodeAuthorizerContextEnvVar = "DD_DECODE_AUTHORIZER_CONTEXT";
3839

3940
interface GlobalConfig {
4041
/**
@@ -66,6 +67,7 @@ export const defaultConfig: Config = {
6667
createInferredSpan: true,
6768
debugLogging: false,
6869
encodeAuthorizerContext: true,
70+
decodeAuthorizerContext: true,
6971
enhancedMetrics: true,
7072
forceWrap: false,
7173
injectLogContext: true,
@@ -280,6 +282,11 @@ function getConfig(userConfig?: Partial<Config>): Config {
280282
config.encodeAuthorizerContext = result === "true";
281283
}
282284

285+
if (userConfig === undefined || userConfig.decodeAuthorizerContext === undefined) {
286+
const result = getEnvValue(decodeAuthorizerContextEnvVar, "true").toLowerCase();
287+
config.decodeAuthorizerContext = result === "true";
288+
}
289+
283290
return config;
284291
}
285292

src/trace/context.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ import {
2626
xrayTraceEnvVar,
2727
} from "./constants";
2828
import { TraceExtractor } from "./listener";
29-
import { eventTypes, parseEventSource, parseEventSourceSubType, eventSubTypes } from "./trigger";
29+
import { parseEventSourceSubType, eventSubTypes } from "./trigger";
3030
import { authorizingRequestIdHeader } from "./constants";
31-
import { datadog } from "../index";
3231

3332
export interface XRayTraceHeader {
3433
traceID: string;
@@ -59,6 +58,7 @@ export function extractTraceContext(
5958
event: any,
6059
context: Context,
6160
extractor?: TraceExtractor,
61+
decodeAuthorizerContext: boolean = true,
6262
): TraceContext | undefined {
6363
let trace;
6464

@@ -74,7 +74,7 @@ export function extractTraceContext(
7474
}
7575

7676
if (!trace) {
77-
trace = readTraceFromEvent(event);
77+
trace = readTraceFromEvent(event, decodeAuthorizerContext);
7878
}
7979

8080
if (!trace) {
@@ -202,7 +202,7 @@ export function sendXraySubsegment(segment: string) {
202202

203203
export function readTraceFromAppSyncEvent(event: any): TraceContext | undefined {
204204
event.headers = event.request.headers;
205-
return readTraceFromHTTPEvent(event);
205+
return readTraceFromHTTPEvent(event, false);
206206
}
207207

208208
export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined {
@@ -365,16 +365,18 @@ export function getInjectedAuthorizerData(event: any, eventSourceSubType: eventS
365365
}
366366
}
367367

368-
export function readTraceFromHTTPEvent(event: any): TraceContext | undefined {
369-
// need to set the trace context if using authorizer lambda in authorizing (non-cached) cases
370-
try {
371-
const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event);
372-
const injectedAuthorizerData = getInjectedAuthorizerData(event, eventSourceSubType);
373-
if (injectedAuthorizerData !== null) {
374-
return exportTraceData(injectedAuthorizerData);
368+
export function readTraceFromHTTPEvent(event: any, decodeAuthorizerContext: boolean = true): TraceContext | undefined {
369+
if (decodeAuthorizerContext) {
370+
// need to set the trace context if using authorizer lambda in authorizing (non-cached) cases
371+
try {
372+
const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event);
373+
const injectedAuthorizerData = getInjectedAuthorizerData(event, eventSourceSubType);
374+
if (injectedAuthorizerData !== null) {
375+
return exportTraceData(injectedAuthorizerData);
376+
}
377+
} catch (error) {
378+
logDebug(`unable to extract trace context from authorizer event.`, { error });
375379
}
376-
} catch (error) {
377-
logDebug(`unable to extract trace context from authorizer event.`, { error });
378380
}
379381

380382
const headers = event.headers;
@@ -390,13 +392,13 @@ export function readTraceFromHTTPEvent(event: any): TraceContext | undefined {
390392
return trace;
391393
}
392394

393-
export function readTraceFromEvent(event: any): TraceContext | undefined {
395+
export function readTraceFromEvent(event: any, decodeAuthorizerContext: boolean = true): TraceContext | undefined {
394396
if (!event || typeof event !== "object") {
395397
return;
396398
}
397399

398400
if (event.headers !== null && typeof event.headers === "object") {
399-
return readTraceFromHTTPEvent(event);
401+
return readTraceFromHTTPEvent(event, decodeAuthorizerContext);
400402
}
401403

402404
if (isSNSEvent(event)) {

src/trace/listener.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ describe("TraceListener", () => {
7878
captureLambdaPayload: false,
7979
createInferredSpan: true,
8080
encodeAuthorizerContext: true,
81+
decodeAuthorizerContext: true,
8182
mergeDatadogXrayTraces: false,
8283
injectLogContext: false,
8384
};

src/trace/listener.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export interface TraceConfig {
3838
* Whether to encode trace context in authorizer metadata
3939
*/
4040
encodeAuthorizerContext: boolean;
41+
/**
42+
* Whether to decode trace context in authorizer metadata
43+
*/
44+
decodeAuthorizerContext: boolean;
4145
/**
4246
* Whether to automatically patch console.log with Datadog's tracing ids.
4347
*/
@@ -90,7 +94,12 @@ export class TraceListener {
9094
} else {
9195
logDebug("Not patching HTTP libraries", { autoPatchHTTP: this.config.autoPatchHTTP, tracerInitialized });
9296
}
93-
const rootTraceHeaders = this.contextService.extractHeadersFromContext(event, context, this.config.traceExtractor);
97+
const rootTraceHeaders = this.contextService.extractHeadersFromContext(
98+
event,
99+
context,
100+
this.config.traceExtractor,
101+
this.config.decodeAuthorizerContext,
102+
);
94103
// The aws.lambda span needs to have a parented to the Datadog trace context from the
95104
// incoming event if available or the X-Ray trace context if hybrid tracing is enabled
96105
let parentSpanContext: SpanContext | undefined;
@@ -104,7 +113,12 @@ export class TraceListener {
104113
});
105114
}
106115
if (this.config.createInferredSpan) {
107-
this.inferredSpan = this.inferrer.createInferredSpan(event, context, parentSpanContext);
116+
this.inferredSpan = this.inferrer.createInferredSpan(
117+
event,
118+
context,
119+
parentSpanContext,
120+
this.config.encodeAuthorizerContext,
121+
);
108122
}
109123
this.lambdaSpanParentContext = this.inferredSpan?.span || parentSpanContext;
110124
this.context = context;

src/trace/span-inferrer.spec.ts

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -510,36 +510,13 @@ describe("Authorizer Spans", () => {
510510
]);
511511
});
512512

513-
it("creates an inferred span for API Gateway V2 event with traced authorizers [Request Type]", () => {
513+
it("connects the inferred span for API Gateway V2 event with traced authorizers [Request Type]", () => {
514514
const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper);
515515
inferrer.createInferredSpan(apiGatewayV2RequestAuthorizer, {} as any, {} as SpanContext);
516516
expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([
517-
"aws.apigateway.authorizer",
518-
{
519-
childOf: {},
520-
startTime: 1665596771812,
521-
tags: {
522-
_inferred_span: { synchronicity: "sync", tag_source: "self" },
523-
apiid: "l9flvsey83",
524-
domain_name: "l9flvsey83.execute-api.sa-east-1.amazonaws.com",
525-
endpoint: "/hello",
526-
"http.method": "GET",
527-
"http.url": "l9flvsey83.execute-api.sa-east-1.amazonaws.com/hello",
528-
operation_name: "aws.apigateway",
529-
request_id: undefined,
530-
"resource.name": "GET /hello",
531-
resource_names: "GET /hello",
532-
service: "l9flvsey83.execute-api.sa-east-1.amazonaws.com",
533-
"service.name": "l9flvsey83.execute-api.sa-east-1.amazonaws.com",
534-
"span.type": "http",
535-
stage: "$default",
536-
},
537-
},
538-
]);
539-
expect(mockWrapperWithFinish.startSpan.mock.calls[1]).toEqual([
540517
"aws.apigateway",
541518
{
542-
childOf: { finish: mockFinish }, // Hack around jest mocks
519+
childOf: {},
543520
startTime: 1665596771812,
544521
tags: {
545522
_inferred_span: { synchronicity: "sync", tag_source: "self" },
@@ -548,7 +525,7 @@ describe("Authorizer Spans", () => {
548525
endpoint: "/hello",
549526
"http.method": "GET",
550527
"http.url": "l9flvsey83.execute-api.sa-east-1.amazonaws.com/hello",
551-
operation_name: "aws.apigateway",
528+
operation_name: "aws.httpapi",
552529
request_id: undefined,
553530
"resource.name": "GET /hello",
554531
resource_names: "GET /hello",

src/trace/span-inferrer.ts

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,26 @@ import { SpanWrapper } from "./span-wrapper";
1414
import { parentSpanFinishTimeHeader } from "./constants";
1515
import { logDebug } from "../utils";
1616
import { getInjectedAuthorizerData } from "./context";
17+
import { decodeAuthorizerContextEnvVar } from "../index";
1718

1819
export class SpanInferrer {
1920
traceWrapper: TracerWrapper;
2021
constructor(traceWrapper: TracerWrapper) {
2122
this.traceWrapper = traceWrapper;
2223
}
2324

24-
public createInferredSpan(event: any, context: Context | undefined, parentSpanContext: SpanContext | undefined): any {
25+
public createInferredSpan(
26+
event: any,
27+
context: Context | undefined,
28+
parentSpanContext: SpanContext | undefined,
29+
decodeAuthorizerContext: boolean = true,
30+
): any {
2531
const eventSource = parseEventSource(event);
2632
if (eventSource === eventTypes.lambdaUrl) {
2733
return this.createInferredSpanForLambdaUrl(event, context);
2834
}
2935
if (eventSource === eventTypes.apiGateway) {
30-
return this.createInferredSpanForApiGateway(event, context, parentSpanContext);
36+
return this.createInferredSpanForApiGateway(event, context, parentSpanContext, decodeAuthorizerContext);
3137
}
3238
if (eventSource === eventTypes.sns) {
3339
return this.createInferredSpanForSns(event, context, parentSpanContext);
@@ -60,6 +66,7 @@ export class SpanInferrer {
6066
event: any,
6167
context: Context | undefined,
6268
parentSpanContext: SpanContext | undefined,
69+
decodeAuthorizerContext: boolean = true,
6370
): SpanWrapper {
6471
const options: SpanOptions = {};
6572
const domain = event.requestContext.domainName;
@@ -102,47 +109,47 @@ export class SpanInferrer {
102109
options.tags.event_type = event.requestContext.eventType;
103110
}
104111
let upstreamAuthorizerSpan: SpanWrapper | undefined;
105-
try {
106-
const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event);
107-
const parsedUpstreamContext = getInjectedAuthorizerData(event, eventSourceSubType);
108-
109-
if (parsedUpstreamContext) {
110-
let upstreamSpanOptions: SpanOptions = {};
111-
const startTime = parsedUpstreamContext[parentSpanFinishTimeHeader] / 1e6;
112-
upstreamSpanOptions = {
113-
startTime,
114-
tags: { operation_name: "aws.apigateway.authorizer", ...options.tags },
115-
};
116-
117-
let endTime: number;
118-
// getting an approximated endTime
119-
if (eventSourceSubType === eventSubTypes.apiGatewayV2) {
120-
endTime = startTime;
121-
} else {
122-
endTime = event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency;
112+
const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event);
113+
if (decodeAuthorizerContext) {
114+
try {
115+
const parsedUpstreamContext = getInjectedAuthorizerData(event, eventSourceSubType);
116+
if (parsedUpstreamContext) {
117+
let upstreamSpanOptions: SpanOptions = {};
118+
const startTime = parsedUpstreamContext[parentSpanFinishTimeHeader] / 1e6;
119+
// getting an approximated endTime
120+
if (eventSourceSubType === eventSubTypes.apiGatewayV2) {
121+
options.startTime = startTime; // not inserting authorizer span
122+
options.tags.operation_name = "aws.httpapi";
123+
} else {
124+
upstreamSpanOptions = {
125+
startTime,
126+
childOf: parentSpanContext,
127+
tags: { operation_name: "aws.apigateway.authorizer", ...options.tags },
128+
};
129+
upstreamAuthorizerSpan = new SpanWrapper(
130+
this.traceWrapper.startSpan("aws.apigateway.authorizer", upstreamSpanOptions),
131+
{ isAsync: false },
132+
);
133+
const endTime = event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency;
134+
upstreamAuthorizerSpan.finish(endTime);
135+
options.startTime = endTime; // For the main function's inferred span
136+
}
123137
}
138+
} catch (error) {
139+
logDebug("Error decoding authorizer span", error as Error);
140+
}
141+
}
124142

125-
upstreamSpanOptions.childOf = parentSpanContext;
126-
upstreamAuthorizerSpan = new SpanWrapper(
127-
this.traceWrapper.startSpan("aws.apigateway.authorizer", upstreamSpanOptions),
128-
{ isAsync: false },
129-
);
130-
upstreamAuthorizerSpan.finish(endTime);
131-
options.startTime = endTime;
143+
if (!options.startTime) {
144+
if (
145+
eventSourceSubType === eventSubTypes.apiGatewayV1 ||
146+
eventSourceSubType === eventSubTypes.apiGatewayWebsocket
147+
) {
148+
options.startTime = event.requestContext.requestTimeEpoch;
132149
} else {
133-
if (
134-
eventSourceSubType === eventSubTypes.apiGatewayV1 ||
135-
eventSourceSubType === eventSubTypes.apiGatewayWebsocket
136-
) {
137-
options.startTime = event.requestContext.requestTimeEpoch;
138-
} else {
139-
options.startTime = event.requestContext.timeEpoch;
140-
}
150+
options.startTime = event.requestContext.timeEpoch;
141151
}
142-
} catch (error) {
143-
logDebug("Error decoding authorizer span", error as Error);
144152
}
145-
146153
options.childOf = upstreamAuthorizerSpan ? upstreamAuthorizerSpan.span : parentSpanContext;
147154

148155
const spanWrapperOptions = {

src/trace/trace-context-service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ export class TraceContextService {
2525
event: any,
2626
context: Context,
2727
extractor?: TraceExtractor,
28+
decodeAuthorizerContext: boolean = true,
2829
): Partial<TraceHeaders> | undefined {
29-
this.rootTraceContext = extractTraceContext(event, context, extractor);
30+
this.rootTraceContext = extractTraceContext(event, context, extractor, decodeAuthorizerContext);
3031
return this.currentTraceHeaders;
3132
}
3233

0 commit comments

Comments
 (0)