Skip to content

Commit 77074a0

Browse files
committed
feat: specify subsegment name when capturing class method
1 parent 06fbaae commit 77074a0

10 files changed

+241
-42
lines changed

Diff for: packages/tracer/src/Tracer.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Handler } from 'aws-lambda';
22
import { AsyncHandler, SyncHandler, Utility } from '@aws-lambda-powertools/commons';
33
import { TracerInterface } from '.';
44
import { ConfigServiceInterface, EnvironmentVariablesService } from './config';
5-
import { HandlerMethodDecorator, TracerOptions, HandlerOptions, MethodDecorator } from './types';
5+
import { HandlerMethodDecorator, TracerOptions, MethodDecorator, CaptureLambdaHandlerOptions, CaptureMethodOptions } from './types';
66
import { ProviderService, ProviderServiceInterface } from './provider';
77
import { Segment, Subsegment } from 'aws-xray-sdk-core';
88

@@ -338,8 +338,9 @@ class Tracer extends Utility implements TracerInterface {
338338
* ```
339339
*
340340
* @decorator Class
341+
* @param options - (_optional_) Options for the decorator
341342
*/
342-
public captureLambdaHandler(options?: HandlerOptions): HandlerMethodDecorator {
343+
public captureLambdaHandler(options?: CaptureLambdaHandlerOptions): HandlerMethodDecorator {
343344
return (_target, _propertyKey, descriptor) => {
344345
/**
345346
* The descriptor.value is the method this decorator decorates, it cannot be undefined.
@@ -418,8 +419,9 @@ class Tracer extends Utility implements TracerInterface {
418419
* ```
419420
*
420421
* @decorator Class
422+
* @param options - (_optional_) Options for the decorator
421423
*/
422-
public captureMethod(options?: HandlerOptions): MethodDecorator {
424+
public captureMethod(options?: CaptureMethodOptions): MethodDecorator {
423425
return (_target, _propertyKey, descriptor) => {
424426
// The descriptor.value is the method this decorator decorates, it cannot be undefined.
425427
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -434,7 +436,9 @@ class Tracer extends Utility implements TracerInterface {
434436
return originalMethod.apply(this, [...args]);
435437
}
436438

437-
return tracerRef.provider.captureAsyncFunc(`### ${originalMethod.name}`, async subsegment => {
439+
const subsegmentName = options?.subSegmentName ? options.subSegmentName : `### ${originalMethod.name}`;
440+
441+
return tracerRef.provider.captureAsyncFunc(subsegmentName, async subsegment => {
438442
let result;
439443
try {
440444
result = await originalMethod.apply(this, [...args]);

Diff for: packages/tracer/src/TracerInterface.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HandlerMethodDecorator, MethodDecorator } from './types';
1+
import { CaptureLambdaHandlerOptions, CaptureMethodOptions, HandlerMethodDecorator, MethodDecorator } from './types';
22
import { Segment, Subsegment } from 'aws-xray-sdk-core';
33

44
interface TracerInterface {
@@ -9,8 +9,8 @@ interface TracerInterface {
99
captureAWS<T>(aws: T): void | T
1010
captureAWSv3Client<T>(service: T): void | T
1111
captureAWSClient<T>(service: T): void | T
12-
captureLambdaHandler(): HandlerMethodDecorator
13-
captureMethod(): MethodDecorator
12+
captureLambdaHandler(options?: CaptureLambdaHandlerOptions): HandlerMethodDecorator
13+
captureMethod(options?: CaptureMethodOptions): MethodDecorator
1414
getSegment(): Segment | Subsegment
1515
isTracingEnabled(): boolean
1616
putAnnotation: (key: string, value: string | number | boolean) => void

Diff for: packages/tracer/src/middleware/middy.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type middy from '@middy/core';
22
import type { Tracer } from '../Tracer';
33
import type { Segment, Subsegment } from 'aws-xray-sdk-core';
4-
import type { HandlerOptions } from '../types';
4+
import type { CaptureLambdaHandlerOptions } from '../types';
55

66
/**
77
* A middy middleware automating capture of metadata and annotations on segments or subsegments for a Lambda Handler.
@@ -25,9 +25,10 @@ import type { HandlerOptions } from '../types';
2525
* ```
2626
*
2727
* @param target - The Tracer instance to use for tracing
28+
* @param options - (_optional_) Options for the middleware
2829
* @returns middleware object - The middy middleware object
2930
*/
30-
const captureLambdaHandler = (target: Tracer, options?: HandlerOptions): middy.MiddlewareObj => {
31+
const captureLambdaHandler = (target: Tracer, options?: CaptureLambdaHandlerOptions): middy.MiddlewareObj => {
3132
let lambdaSegment: Subsegment | Segment;
3233

3334
const open = (): void => {

Diff for: packages/tracer/src/types/Tracer.ts

+54-4
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,70 @@ type TracerOptions = {
2929
/**
3030
* Options for handler decorators and middleware.
3131
*
32-
* Usage:
32+
* Options supported:
33+
* * `captureResponse` - (_optional_) - Disable response serialization as subsegment metadata
34+
*
35+
* Middleware usage:
36+
* @example
37+
* ```typescript
38+
* import middy from '@middy/core';
39+
*
40+
* const tracer = new Tracer();
41+
*
42+
* const lambdaHandler = async (_event: any, _context: any): Promise<void> => {};
43+
*
44+
* export const handler = middy(lambdaHandler)
45+
* .use(captureLambdaHandler(tracer, { captureResponse: false }));
46+
* ```
47+
*
48+
* Decorator usage:
3349
* @example
3450
* ```typescript
3551
* const tracer = new Tracer();
3652
*
3753
* class Lambda implements LambdaInterface {
3854
* @tracer.captureLambdaHandler({ captureResponse: false })
39-
* async handler(_event: any, _context: any): Promise<void> {}
55+
* public async handler(_event: any, _context: any): Promise<void> {}
56+
* }
57+
*
58+
* const handlerClass = new Lambda();
59+
* export const handler = handlerClass.handler.bind(handlerClass);
60+
* ```
61+
*/
62+
type CaptureLambdaHandlerOptions = {
63+
captureResponse?: boolean
64+
};
65+
66+
/**
67+
* Options for method decorators.
68+
*
69+
* Options supported:
70+
* * `subSegmentName` - (_optional_) - Set a custom name for the subsegment
71+
* * `captureResponse` - (_optional_) - Disable response serialization as subsegment metadata
72+
*
73+
* Usage:
74+
* @example
75+
* ```typescript
76+
* const tracer = new Tracer();
77+
*
78+
* class Lambda implements LambdaInterface {
79+
* @tracer.captureMethod({ subSegmentName: 'gettingChargeId', captureResponse: false })
80+
* private getChargeId(): string {
81+
* return 'foo bar';
82+
* }
83+
*
84+
* @tracer.captureLambdaHandler({ subSegmentName: '', captureResponse: false })
85+
* public async handler(_event: any, _context: any): Promise<void> {
86+
* this.getChargeId();
87+
* }
4088
* }
4189
*
4290
* const handlerClass = new Lambda();
4391
* export const handler = handlerClass.handler.bind(handlerClass);
4492
* ```
4593
*/
46-
type HandlerOptions = {
94+
type CaptureMethodOptions = {
95+
subSegmentName?: string
4796
captureResponse?: boolean
4897
};
4998

@@ -59,7 +108,8 @@ type MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: T
59108

60109
export {
61110
TracerOptions,
62-
HandlerOptions,
111+
CaptureLambdaHandlerOptions,
112+
CaptureMethodOptions,
63113
HandlerMethodDecorator,
64114
MethodDecorator
65115
};

Diff for: packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ const serviceName = process.env.EXPECTED_SERVICE_NAME ?? 'MyFunctionWithStandard
99
const customAnnotationKey = process.env.EXPECTED_CUSTOM_ANNOTATION_KEY ?? 'myAnnotation';
1010
const customAnnotationValue = process.env.EXPECTED_CUSTOM_ANNOTATION_VALUE ?? 'myValue';
1111
const customMetadataKey = process.env.EXPECTED_CUSTOM_METADATA_KEY ?? 'myMetadata';
12-
const customMetadataValue = JSON.parse(process.env.EXPECTED_CUSTOM_METADATA_VALUE) ?? { bar: 'baz' };
13-
const customResponseValue = JSON.parse(process.env.EXPECTED_CUSTOM_RESPONSE_VALUE) ?? { foo: 'bar' };
12+
const customMetadataValue = process.env.EXPECTED_CUSTOM_METADATA_VALUE ? JSON.parse(process.env.EXPECTED_CUSTOM_METADATA_VALUE) : { bar: 'baz' };
13+
const customResponseValue = process.env.EXPECTED_CUSTOM_RESPONSE_VALUE ? JSON.parse(process.env.EXPECTED_CUSTOM_RESPONSE_VALUE) : { foo: 'bar' };
1414
const customErrorMessage = process.env.EXPECTED_CUSTOM_ERROR_MESSAGE ?? 'An error has occurred';
1515
const testTableName = process.env.TEST_TABLE_NAME ?? 'TestTable';
1616

@@ -42,8 +42,6 @@ export class MyFunctionBase {
4242
this.returnValue = customResponseValue;
4343
}
4444

45-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
46-
// @ts-ignore
4745
public handler(event: CustomEvent, _context: Context, _callback: Callback<unknown>): void | Promise<unknown> {
4846
tracer.putAnnotation(customAnnotationKey, customAnnotationValue);
4947
tracer.putMetadata(customMetadataKey, customMetadataValue);
@@ -78,8 +76,6 @@ export class MyFunctionBase {
7876
});
7977
}
8078

81-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
82-
// @ts-ignore
8379
public myMethod(): string {
8480
return this.returnValue;
8581
}
@@ -121,4 +117,4 @@ class MyFunctionWithDecoratorCaptureResponseFalse extends MyFunctionBase {
121117
}
122118

123119
const handlerWithCaptureResponseFalseClass = new MyFunctionWithDecoratorCaptureResponseFalse();
124-
export const handlerWithCaptureResponseFalse = handlerClass.handler.bind(handlerWithCaptureResponseFalseClass);
120+
export const handlerWithCaptureResponseFalse = handlerWithCaptureResponseFalseClass.handler.bind(handlerWithCaptureResponseFalseClass);

Diff for: packages/tracer/tests/e2e/allFeatures.decorator.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ let startTime: Date;
6262
* Function #1 is with all flags enabled.
6363
*/
6464
const uuidFunction1 = v4();
65-
const functionNameWithAllFlagsEnabled = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction1, runtime, 'AllFeatures-Decoratory-AllFlagsEnabled');
65+
const functionNameWithAllFlagsEnabled = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction1, runtime, 'AllFeatures-Decorator-AllFlagsEnabled');
6666
const serviceNameWithAllFlagsEnabled = functionNameWithAllFlagsEnabled;
6767

6868
/**
@@ -79,7 +79,7 @@ const functionNameWithTracerDisabled = generateUniqueName(RESOURCE_NAME_PREFIX,
7979
const serviceNameWithTracerDisabled = functionNameWithNoCaptureErrorOrResponse;
8080

8181
/**
82-
* Function #4 disables tracer
82+
* Function #4 disables capture response via decorator options
8383
*/
8484
const uuidFunction4 = v4();
8585
const functionNameWithCaptureResponseFalse = generateUniqueName(RESOURCE_NAME_PREFIX, uuidFunction4, runtime, 'AllFeatures-Decorator-CaptureResponseFalse');

Diff for: packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts

+38-8
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ const serviceName = process.env.EXPECTED_SERVICE_NAME ?? 'MyFunctionWithStandard
99
const customAnnotationKey = process.env.EXPECTED_CUSTOM_ANNOTATION_KEY ?? 'myAnnotation';
1010
const customAnnotationValue = process.env.EXPECTED_CUSTOM_ANNOTATION_VALUE ?? 'myValue';
1111
const customMetadataKey = process.env.EXPECTED_CUSTOM_METADATA_KEY ?? 'myMetadata';
12-
const customMetadataValue = JSON.parse(process.env.EXPECTED_CUSTOM_METADATA_VALUE) ?? { bar: 'baz' };
13-
const customResponseValue = JSON.parse(process.env.EXPECTED_CUSTOM_RESPONSE_VALUE) ?? { foo: 'bar' };
12+
const customMetadataValue = process.env.EXPECTED_CUSTOM_METADATA_VALUE ? JSON.parse(process.env.EXPECTED_CUSTOM_METADATA_VALUE) : { bar: 'baz' };
13+
const customResponseValue = process.env.EXPECTED_CUSTOM_RESPONSE_VALUE ? JSON.parse(process.env.EXPECTED_CUSTOM_RESPONSE_VALUE) : { foo: 'bar' };
1414
const customErrorMessage = process.env.EXPECTED_CUSTOM_ERROR_MESSAGE ?? 'An error has occurred';
1515
const testTableName = process.env.TEST_TABLE_NAME ?? 'TestTable';
16+
const customSubSegmentName = process.env.EXPECTED_CUSTOM_SUBSEGMENT_NAME ?? 'mySubsegment';
1617

1718
interface CustomEvent {
1819
throw: boolean
@@ -35,16 +36,13 @@ const refreshAWSSDKImport = (): void => {
3536
const tracer = new Tracer({ serviceName: serviceName });
3637
const dynamoDBv3 = tracer.captureAWSv3Client(new DynamoDBClient({}));
3738

38-
export class MyFunctionWithDecorator {
39+
export class MyFunctionBase {
3940
private readonly returnValue: string;
4041

4142
public constructor() {
4243
this.returnValue = customResponseValue;
4344
}
4445

45-
@tracer.captureLambdaHandler()
46-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
47-
// @ts-ignore
4846
public async handler(event: CustomEvent, _context: Context): Promise<unknown> {
4947
tracer.putAnnotation(customAnnotationKey, customAnnotationValue);
5048
tracer.putMetadata(customMetadataKey, customMetadataValue);
@@ -74,13 +72,45 @@ export class MyFunctionWithDecorator {
7472
}
7573
}
7674

75+
public myMethod(): string {
76+
return this.returnValue;
77+
}
78+
}
79+
80+
class MyFunctionWithDecorator extends MyFunctionBase {
81+
@tracer.captureLambdaHandler()
82+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
83+
// @ts-ignore
84+
public async handler(event: CustomEvent, _context: Context, _callback: Callback<unknown>): void | Promise<unknown> {
85+
return super.handler(event, _context);
86+
}
87+
7788
@tracer.captureMethod()
7889
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
7990
// @ts-ignore
8091
public myMethod(): string {
81-
return this.returnValue;
92+
return super.myMethod();
8293
}
8394
}
8495

8596
const handlerClass = new MyFunctionWithDecorator();
86-
export const handler = handlerClass.handler.bind(handlerClass);
97+
export const handler = handlerClass.handler.bind(handlerClass);
98+
99+
export class MyFunctionWithDecoratorAndCustomNamedSubSegmentForMethod extends MyFunctionBase {
100+
@tracer.captureLambdaHandler()
101+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
102+
// @ts-ignore
103+
public async handler(event: CustomEvent, _context: Context, _callback: Callback<unknown>): void | Promise<unknown> {
104+
return super.handler(event, _context);
105+
}
106+
107+
@tracer.captureMethod({ subSegmentName: customSubSegmentName })
108+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
109+
// @ts-ignore
110+
public myMethod(): string {
111+
return super.myMethod();
112+
}
113+
}
114+
115+
const handlerWithCustomSubsegmentNameInMethodClass = new MyFunctionWithDecoratorAndCustomNamedSubSegmentForMethod();
116+
export const handlerWithCustomSubsegmentNameInMethod = handlerClass.handler.bind(handlerWithCustomSubsegmentNameInMethodClass);

0 commit comments

Comments
 (0)