Skip to content

Commit a82fc3a

Browse files
authored
refactor(tracer): log warning instead of throwing when segment is not found (#1370)
* improv: moved logic to provider + changed middleware behavior around absent segment * tests: fixed tests for middleware * tests: fixed tests for main class * tests: fixed provider service tests * chore: bump xray-sdk-core * chore: update jsdoc for addErrorAsMetadata * chore: update jsdoc for getSegment * chore: update jsdoc for getSegment
1 parent 79a321b commit a82fc3a

File tree

10 files changed

+391
-466
lines changed

10 files changed

+391
-466
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/tracer/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
},
5353
"dependencies": {
5454
"@aws-lambda-powertools/commons": "^1.6.0",
55-
"aws-xray-sdk-core": "^3.4.0"
55+
"aws-xray-sdk-core": "^3.4.1"
5656
},
5757
"keywords": [
5858
"aws",
@@ -63,4 +63,4 @@
6363
"serverless",
6464
"nodejs"
6565
]
66-
}
66+
}

packages/tracer/src/Tracer.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { Segment, Subsegment } from 'aws-xray-sdk-core';
3838
*
3939
* const tracer = new Tracer({ serviceName: 'serverlessAirline' });
4040
*
41-
* const lambdaHandler = async (_event: any, _context: any) => {
41+
* const lambdaHandler = async (_event: any, _context: any) => {
4242
* ...
4343
* };
4444
*
@@ -153,20 +153,26 @@ class Tracer extends Utility implements TracerInterface {
153153
* @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-errors
154154
*
155155
* @param error - Error to serialize as metadata
156+
* @param [remote] - Whether the error was thrown by a remote service. Defaults to `false`
156157
*/
157-
public addErrorAsMetadata(error: Error): void {
158+
public addErrorAsMetadata(error: Error, remote?: boolean): void {
158159
if (!this.isTracingEnabled()) {
159160
return;
160161
}
161162

162163
const subsegment = this.getSegment();
164+
if (subsegment === undefined) {
165+
166+
return;
167+
}
168+
163169
if (!this.captureError) {
164170
subsegment.addErrorFlag();
165171

166172
return;
167173
}
168174

169-
subsegment.addError(error, false);
175+
subsegment.addError(error, remote || false);
170176
}
171177

172178
/**
@@ -506,7 +512,7 @@ class Tracer extends Utility implements TracerInterface {
506512
}
507513

508514
/**
509-
* Get the active segment or subsegment in the current scope.
515+
* Get the active segment or subsegment (if any) in the current scope.
510516
*
511517
* Usually you won't need to call this method unless you are creating custom subsegments or using manual mode.
512518
*
@@ -525,15 +531,17 @@ class Tracer extends Utility implements TracerInterface {
525531
* }
526532
* ```
527533
*
528-
* @returns segment - The active segment or subsegment in the current scope.
534+
* @returns The active segment or subsegment in the current scope. Will log a warning and return `undefined` if no segment is found.
529535
*/
530-
public getSegment(): Segment | Subsegment {
536+
public getSegment(): Segment | Subsegment | undefined {
531537
if (!this.isTracingEnabled()) {
532538
return new Subsegment('## Dummy segment');
533539
}
534540
const segment = this.provider.getSegment();
535541
if (segment === undefined) {
536-
throw new Error('Failed to get the current sub/segment from the context.');
542+
console.warn(
543+
'Failed to get the current sub/segment from the context, this is likely because you are not using the Tracer in a Lambda function.'
544+
);
537545
}
538546

539547
return segment;
@@ -573,13 +581,7 @@ class Tracer extends Utility implements TracerInterface {
573581
public putAnnotation(key: string, value: string | number | boolean): void {
574582
if (!this.isTracingEnabled()) return;
575583

576-
const document = this.getSegment();
577-
if (document instanceof Segment) {
578-
console.warn('You cannot annotate the main segment in a Lambda execution environment');
579-
580-
return;
581-
}
582-
document.addAnnotation(key, value);
584+
this.provider.putAnnotation(key, value);
583585
}
584586

585587
/**
@@ -606,15 +608,7 @@ class Tracer extends Utility implements TracerInterface {
606608
public putMetadata(key: string, value: unknown, namespace?: string | undefined): void {
607609
if (!this.isTracingEnabled()) return;
608610

609-
const document = this.getSegment();
610-
if (document instanceof Segment) {
611-
console.warn('You cannot add metadata to the main segment in a Lambda execution environment');
612-
613-
return;
614-
}
615-
616-
namespace = namespace || this.serviceName;
617-
document.addMetadata(key, value, namespace);
611+
this.provider.putMetadata(key, value, namespace || this.serviceName);
618612
}
619613

620614
/**

packages/tracer/src/TracerInterface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { CaptureLambdaHandlerOptions, CaptureMethodOptions, HandlerMethodDecorat
22
import { Segment, Subsegment } from 'aws-xray-sdk-core';
33

44
interface TracerInterface {
5-
addErrorAsMetadata(error: Error): void
5+
addErrorAsMetadata(error: Error, remote?: boolean): void
66
addResponseAsMetadata(data?: unknown, methodName?: string): void
77
addServiceNameAnnotation(): void
88
annotateColdStart(): void
@@ -11,7 +11,7 @@ interface TracerInterface {
1111
captureAWSClient<T>(service: T): void | T
1212
captureLambdaHandler(options?: CaptureLambdaHandlerOptions): HandlerMethodDecorator
1313
captureMethod(options?: CaptureMethodOptions): MethodDecorator
14-
getSegment(): Segment | Subsegment
14+
getSegment(): Segment | Subsegment | undefined
1515
getRootXrayTraceId(): string | undefined
1616
isTracingEnabled(): boolean
1717
putAnnotation: (key: string, value: string | number | boolean) => void

packages/tracer/src/middleware/middy.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,26 @@ import type {
3434
* @returns middleware - The middy middleware object
3535
*/
3636
const captureLambdaHandler = (target: Tracer, options?: CaptureLambdaHandlerOptions): MiddlewareLikeObj => {
37-
let lambdaSegment: Subsegment | Segment;
37+
let lambdaSegment: Segment;
38+
let handlerSegment: Subsegment;
3839

3940
const open = (): void => {
40-
lambdaSegment = target.getSegment();
41-
const handlerSegment = lambdaSegment.addNewSubsegment(`## ${process.env._HANDLER}`);
41+
const segment = target.getSegment();
42+
if (segment === undefined) {
43+
return;
44+
}
45+
// If segment is defined, then it is a Segment as this middleware is only used for Lambda Handlers
46+
lambdaSegment = segment as Segment;
47+
handlerSegment = lambdaSegment.addNewSubsegment(`## ${process.env._HANDLER}`);
4248
target.setSegment(handlerSegment);
4349
};
4450

4551
const close = (): void => {
46-
const subsegment = target.getSegment();
47-
subsegment.close();
48-
target.setSegment(lambdaSegment as Segment);
52+
if (handlerSegment === undefined || lambdaSegment === null) {
53+
return;
54+
}
55+
handlerSegment.close();
56+
target.setSegment(lambdaSegment);
4957
};
5058

5159
const captureLambdaHandlerBefore = async (): Promise<void> => {

packages/tracer/src/provider/ProviderService.ts

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
1-
import { ContextMissingStrategy } from 'aws-xray-sdk-core/dist/lib/context_utils';
1+
import {
2+
ContextMissingStrategy
3+
} from 'aws-xray-sdk-core/dist/lib/context_utils';
24
import { Namespace } from 'cls-hooked';
35
import { ProviderServiceInterface } from '.';
4-
import { captureAWS, captureAWSClient, captureAWSv3Client, captureAsyncFunc, captureFunc, captureHTTPsGlobal, getNamespace, getSegment, setSegment, Segment, Subsegment, setContextMissingStrategy, setDaemonAddress, setLogger, Logger } from 'aws-xray-sdk-core';
6+
import {
7+
captureAWS,
8+
captureAWSClient,
9+
captureAWSv3Client,
10+
captureAsyncFunc,
11+
captureFunc,
12+
captureHTTPsGlobal,
13+
getNamespace,
14+
getSegment,
15+
setSegment,
16+
Segment,
17+
Subsegment,
18+
setContextMissingStrategy,
19+
setDaemonAddress,
20+
setLogger,
21+
Logger
22+
} from 'aws-xray-sdk-core';
523

624
class ProviderService implements ProviderServiceInterface {
7-
825
public captureAWS<T>(awssdk: T): T {
926
return captureAWS(awssdk);
1027
}
@@ -42,6 +59,36 @@ class ProviderService implements ProviderServiceInterface {
4259
return getSegment();
4360
}
4461

62+
public putAnnotation(key: string, value: string | number | boolean): void {
63+
const segment = this.getSegment();
64+
if (segment === undefined) {
65+
console.warn('No active segment or subsegment found, skipping annotation');
66+
67+
return;
68+
}
69+
if (segment instanceof Segment) {
70+
console.warn('You cannot annotate the main segment in a Lambda execution environment');
71+
72+
return;
73+
}
74+
segment.addAnnotation(key, value);
75+
}
76+
77+
public putMetadata(key: string, value: unknown, namespace?: string): void {
78+
const segment = this.getSegment();
79+
if (segment === undefined) {
80+
console.warn('No active segment or subsegment found, skipping metadata addition');
81+
82+
return;
83+
}
84+
if (segment instanceof Segment) {
85+
console.warn('You cannot add metadata to the main segment in a Lambda execution environment');
86+
87+
return;
88+
}
89+
segment.addMetadata(key, value, namespace);
90+
}
91+
4592
public setContextMissingStrategy(strategy: unknown): void {
4693
setContextMissingStrategy(strategy as ContextMissingStrategy);
4794
}

packages/tracer/src/provider/ProviderServiceInterface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ interface ProviderServiceInterface {
2525
captureFunc(name: string, fcn: (subsegment?: Subsegment) => unknown, parent?: Segment | Subsegment): unknown
2626

2727
captureHTTPsGlobal(): void
28+
29+
putAnnotation(key: string, value: string | number | boolean): void
30+
31+
putMetadata(key: string, value: unknown, namespace?: string): void
2832
}
2933

3034
export {

0 commit comments

Comments
 (0)