From 7e58cb6262211fe0fb293c85c3a99ecc524f69ea Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 6 Jan 2022 23:48:29 +0100 Subject: [PATCH 1/5] fix: updated CDK examples to correct errors & improved comments/structure --- .../cdk/lib/example-function.MyFunction.ts | 49 ++++++---- ...xample-function.MyFunctionWithDecorator.ts | 50 ++++++----- .../example-function.MyFunctionWithMiddy.ts | 90 ++++++++++++------- ...le-function.Tracer.CaptureErrorDisabled.ts | 4 +- ...function.Tracer.CaptureResponseDisabled.ts | 2 +- .../lib/example-function.Tracer.Decorator.ts | 3 +- .../lib/example-function.Tracer.Disabled.ts | 12 +-- .../cdk/lib/example-function.Tracer.Manual.ts | 3 +- .../lib/example-function.Tracer.Middleware.ts | 17 ++-- .../example-function.Tracer.PatchAWSSDKv2.ts | 24 +++-- .../example-function.Tracer.PatchAWSSDKv3.ts | 22 +++-- .../example-function.Tracer.PatchAllAWSSDK.ts | 24 +++-- 12 files changed, 187 insertions(+), 113 deletions(-) diff --git a/examples/cdk/lib/example-function.MyFunction.ts b/examples/cdk/lib/example-function.MyFunction.ts index 9d00fa2b3d..560ca6724d 100644 --- a/examples/cdk/lib/example-function.MyFunction.ts +++ b/examples/cdk/lib/example-function.MyFunction.ts @@ -6,19 +6,24 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; const namespace = 'CDKExample'; const serviceName = 'MyFunctionWithStandardHandler'; -const metrics = new Metrics({ namespace: namespace, service: serviceName }); +const metrics = new Metrics({ namespace: namespace, serviceName: serviceName }); const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName }); const tracer = new Tracer({ serviceName: serviceName }); -export const handler = async (_event: unknown, context: Context): Promise => { +export const handler = async (event: unknown, context: Context): Promise => { // Since we are in manual mode we need to create the handler segment (the 4 lines below would be done for you by decorator/middleware) // we do it at the beginning because we want to trace the whole duration of the handler - const segment = tracer.getSegment(); // This is the facade segment (the one that is created by Lambda & that can't be manipulated) - const handlerSegment = segment.addNewSubsegment(`## ${context.functionName}`); - // TODO: expose tracer.annotateColdStart() - tracer.putAnnotation('ColdStart', Tracer.coldStart); + const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) + // Create subsegment for the function & set it as active + const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`); + tracer.setSegment(handlerSegment); - // ### Experiment logger + // Annotate the subsegment with the cold start & serviceName + tracer.annotateColdStart(); + tracer.addServiceNameAnnotation(); + + // ### Experiment with Logger + logger.addContext(context); logger.addPersistentLogAttributes({ testKey: 'testValue', }); @@ -27,7 +32,7 @@ export const handler = async (_event: unknown, context: Context): Promise logger.warn('This is an WARN log'); logger.error('This is an ERROR log'); - // ### Experiment metrics + // ### Experiment with Metrics metrics.captureColdStartMetric(); metrics.raiseOnEmptyMetrics(); metrics.setDefaultDimensions({ environment: 'example', type: 'standardFunction' }); @@ -38,24 +43,30 @@ export const handler = async (_event: unknown, context: Context): Promise metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50); metrics.publishStoredMetrics(); - metrics.raiseOnEmptyMetrics(); - - // ### Experiment tracer + metrics.throwOnEmptyMetrics(); - tracer.putAnnotation('Myannotation', 'My annotation\'s value'); + // ### Experiment with Tracer + // This annotation & metadata will be added to the handlerSegment subsegment (## index.handler) + tracer.putAnnotation('awsRequestId', context.awsRequestId); + tracer.putMetadata('eventPayload', event); - // Create subsegment & set it as active - const subsegment = handlerSegment.addNewSubsegment('MySubSegment'); + // Create another subsegment & set it as active + const subsegment = handlerSegment.addNewSubsegment('### MySubSegment'); + tracer.setSegment(subsegment); + let res; try { - throw new Error('test'); - // Add the response as metadata + res = { foo: 'bar' }; + tracer.addResponseAsMetadata(res, process.env._HANDLER); } catch (err) { // Add the error as metadata subsegment.addError(err as Error, false); + } finally { + // Close subsegments (the AWS Lambda one is closed automatically) + subsegment.close(); // (### MySubSegment) + handlerSegment.close(); // (## index.handler) + // Set the facade segment as active again (the one created by AWS Lambda) + tracer.setSegment(segment); } - // Close subsegment - subsegment.close(); - handlerSegment.close(); }; diff --git a/examples/cdk/lib/example-function.MyFunctionWithDecorator.ts b/examples/cdk/lib/example-function.MyFunctionWithDecorator.ts index 9715539f74..a12c3892a9 100644 --- a/examples/cdk/lib/example-function.MyFunctionWithDecorator.ts +++ b/examples/cdk/lib/example-function.MyFunctionWithDecorator.ts @@ -1,24 +1,26 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; -import { Callback, Context } from 'aws-lambda'; +import { Context } from 'aws-lambda'; +import { Events, LambdaInterface } from '@aws-lambda-powertools/commons'; import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; import { Logger } from '@aws-lambda-powertools/logger'; const namespace = 'CDKExample'; const serviceName = 'MyFunctionWithDecorator'; -const metrics = new Metrics({ namespace: namespace, service: serviceName }); +const metrics = new Metrics({ namespace: namespace, serviceName: serviceName }); const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName }); const tracer = new Tracer({ serviceName: serviceName }); -export class MyFunctionWithDecorator { - @tracer.captureLambdaHanlder() +export class MyFunctionWithDecorator implements LambdaInterface { + // We decorate the handler with the various decorators + @tracer.captureLambdaHandler() @logger.injectLambdaContext() @metrics.logMetrics({ captureColdStartMetric: true, - raiseOnEmptyMetrics: true, + throwOnEmptyMetrics: true, defaultDimensions: { environment: 'example', type: 'withDecorator' }, }) - public handler(_event: unknown, _context: Context, _callback: Callback): void | Promise { + public async handler(event: typeof Events.Custom.CustomEvent, context: Context): Promise { // ### Experiment logger logger.addPersistentLogAttributes({ testKey: 'testValue', @@ -36,28 +38,34 @@ export class MyFunctionWithDecorator { metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50); // ### Experiment tracer - tracer.putAnnotation('Myannotation', 'My annotation\'s value'); - // Create subsegment & set it as active - const segment = tracer.getSegment(); // This is the facade segment (the one that is created by Lambda & that can't be manipulated) - const subsegment = segment.addNewSubsegment('MySubSegment'); + // Service & Cold Start annotations will be added for you by the decorator/middleware + // These traces will be added to the main segment (## index.handler) + tracer.putAnnotation('awsRequestId', context.awsRequestId); + tracer.putMetadata('eventPayload', event); + + // Create another subsegment & set it as active + const handlerSegment = tracer.getSegment(); // This is the custom segment created by Tracer for you (## index.handler) + const subsegment = handlerSegment.addNewSubsegment('### MySubSegment'); tracer.setSegment(subsegment); - // TODO: Add the ColdStart annotation !!! NOT POSSIBLE - // tracer.putAnnotation('ColdStart', tracer); + let res; try { - throw new Error('test'); - // Add the response as metadata + res = { foo: 'bar' }; } catch (err) { - // Add the error as metadata - subsegment.addError(err as Error, false); + throw err; + } finally { + // Close the subsegment you created (### MySubSegment) + subsegment.close(); + // Set back the original segment as active (## index.handler) + tracer.setSegment(handlerSegment); + // The main segment (facade) will be closed for you at the end by the decorator/middleware } - - // Close subsegment - subsegment.close(); + + return res; } } -export const handlerClass = new MyFunctionWithDecorator(); -export const handler = handlerClass.handler; +export const myFunction = new MyFunctionWithDecorator(); +export const handler = myFunction.handler; diff --git a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts index 297f5bbda3..5c4a7529cc 100644 --- a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts +++ b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts @@ -1,43 +1,69 @@ import middy from '@middy/core'; -import { Callback, Context } from 'aws-lambda'; -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Context } from 'aws-lambda'; +import { Events } from '@aws-lambda-powertools/commons'; +import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics'; +import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer'; +import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger'; -const metrics = new Metrics({ namespace: 'CDKExample', service: 'withMiddy' }); // Sets metric namespace, and service as a metric dimension +const namespace = 'CDKExample'; +const serviceName = 'MyFunctionWithMiddyMiddleware'; -type CustomEvent = { - throw: boolean -}; +const metrics = new Metrics({ namespace: namespace, serviceName: serviceName }); +const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName }); +const tracer = new Tracer({ serviceName: serviceName }); -class MyFunctionWithDecorator { +const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: Context) => { + // ### Experiment logger + logger.addPersistentLogAttributes({ + testKey: 'testValue', + }); + logger.debug('This is an DEBUG log'); // Won't show by default + logger.info('This is an INFO log'); + logger.warn('This is an WARN log'); + logger.error('This is an ERROR log'); - @metrics.logMetrics({ captureColdStartMetric: true }) - public handler(_event: CustomEvent, _context: Context, _callback: Callback): void | Promise { - metrics.addMetric('test-metric', MetricUnits.Count, 10); - if (_event.throw) { - throw new Error('Test error'); - } - } -} - -const handler = middy(async (_event, _context) => { + // ### Experiment metrics + metrics.addMetric('test-metric', MetricUnits.Count, 10); - const handlerClass = new MyFunctionWithDecorator(); + const metricWithItsOwnDimensions = metrics.singleMetric(); + metricWithItsOwnDimensions.addDimension('InnerDimension', 'true'); + metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50); + + // ### Experiment tracer - return handlerClass.handler(_event, _context, () => console.log('Lambda invoked!')); -}); + // Service & Cold Start annotations will be added for you by the decorator/middleware -handler.before(async (_request) => { - metrics.addMetric('beforeHandlerCalled', MetricUnits.Count, 1); -}); + // These traces will be added to the main segment (## index.handler) + tracer.putAnnotation('awsRequestId', context.awsRequestId); + tracer.putMetadata('eventPayload', event); -handler.after(async (_request) => { - // Won't be flushed since happens after - metrics.addMetric('afterHandlerCalled', MetricUnits.Count, 1); + // Create another subsegment & set it as active + const handlerSegment = tracer.getSegment(); // This is the custom segment created by Tracer for you (## index.handler) + const subsegment = handlerSegment.addNewSubsegment('### MySubSegment'); + tracer.setSegment(subsegment); -}); - -handler.onError(async (_request) => { - metrics.addMetric('onErrorHandlerCalled', MetricUnits.Count, 1); -}); + let res; + try { + res = { foo: 'bar' }; + } catch (err) { + throw err; + } finally { + // Close the subsegment you created (### MySubSegment) + subsegment.close(); + // Set back the original segment as active (## index.handler) + tracer.setSegment(handlerSegment); + // The main segment (facade) will be closed for you at the end by the decorator/middleware + } + + return res; +} -module.exports = { handler }; \ No newline at end of file +// We instrument the handler with the various Middy middlewares +export const handler = middy(lambdaHandler) + .use(captureLambdaHandler(tracer)) + .use(logMetrics(metrics, { + captureColdStartMetric: true, + throwOnEmptyMetrics: true, + defaultDimensions: { environment: 'example', type: 'withDecorator' }, + })) + .use(injectLambdaContext(logger)); \ No newline at end of file diff --git a/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts b/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts index 4ef338b716..f96b5dd32d 100644 --- a/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts +++ b/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts @@ -3,11 +3,11 @@ import { Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer'; -// Set environment variable to disable capture response +// Set environment variable to disable capture response - https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html process.env.POWERTOOLS_TRACER_ERROR_RESPONSE = 'false'; const tracer = new Tracer({ serviceName: 'tracerCaptureErrorDisabledFn' }); -// In this example we are using the middleware pattern but you could use also the captureLambdaHandler decorator +// In this example we are using the middy middleware pattern but you can instrument your functions also with the captureLambdaHandler decorator & manual instrumentation export const handler = middy(async (event: typeof Events.Custom.CustomEvent, context: Context) => { tracer.putAnnotation('awsRequestId', context.awsRequestId); tracer.putMetadata('eventPayload', event); diff --git a/examples/cdk/lib/example-function.Tracer.CaptureResponseDisabled.ts b/examples/cdk/lib/example-function.Tracer.CaptureResponseDisabled.ts index 7d36290cc8..9dab5ffb1e 100644 --- a/examples/cdk/lib/example-function.Tracer.CaptureResponseDisabled.ts +++ b/examples/cdk/lib/example-function.Tracer.CaptureResponseDisabled.ts @@ -3,7 +3,7 @@ import { Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer'; -// Set environment variable to disable capture response +// Set environment variable to disable capture response - https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; const tracer = new Tracer({ serviceName: 'tracerCaptureResponseDisabledFn' }); diff --git a/examples/cdk/lib/example-function.Tracer.Decorator.ts b/examples/cdk/lib/example-function.Tracer.Decorator.ts index d1c7f203b5..1294c2adf9 100644 --- a/examples/cdk/lib/example-function.Tracer.Decorator.ts +++ b/examples/cdk/lib/example-function.Tracer.Decorator.ts @@ -2,8 +2,9 @@ import { Callback, Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { Tracer } from '@aws-lambda-powertools/tracer'; -// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor const tracer = new Tracer({ serviceName: 'tracerDecoratorFn' }); +// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable +// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html export class MyFunctionWithDecorator { // We instrument the handler with the decorator and the tracer will automatically create a subsegment and capture relevant annotations and metadata diff --git a/examples/cdk/lib/example-function.Tracer.Disabled.ts b/examples/cdk/lib/example-function.Tracer.Disabled.ts index 15cd3e8e31..866b3a73b7 100644 --- a/examples/cdk/lib/example-function.Tracer.Disabled.ts +++ b/examples/cdk/lib/example-function.Tracer.Disabled.ts @@ -3,15 +3,15 @@ import { Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer'; -// process.env.POWERTOOLS_TRACE_ENABLED = 'false'; // Alternative to disabling tracing in the constructor +// Disable Tracer by setting POWERTOOLS_TRACE_ENABLED = 'false' in the function environment variables - https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html const tracer = new Tracer({ serviceName: 'tracerDisabledFn', enabled: false }); -// In this example we are using the middleware pattern but you could use also the captureLambdaHandler decorator or the manual mode -export const handler = middy(async (event: typeof Events.Custom.CustomEvent, context: Context) => { +// In this example we are using the middleware pattern but the same applies also the captureLambdaHandler decorator or to manual instrumentation +const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: Context) => { // No tracing will be done and the commands will be ignored, this is useful for testing tracer.putAnnotation('awsRequestId', context.awsRequestId); tracer.putMetadata('eventPayload', event); - + let res; try { res = { foo: 'bar' }; @@ -20,4 +20,6 @@ export const handler = middy(async (event: typeof Events.Custom.CustomEvent, con } return res; -}).use(captureLambdaHandler(tracer)); \ No newline at end of file +} + +export const handler = middy(lambdaHandler).use(captureLambdaHandler(tracer)); \ No newline at end of file diff --git a/examples/cdk/lib/example-function.Tracer.Manual.ts b/examples/cdk/lib/example-function.Tracer.Manual.ts index 99bad242cb..036bb990fa 100644 --- a/examples/cdk/lib/example-function.Tracer.Manual.ts +++ b/examples/cdk/lib/example-function.Tracer.Manual.ts @@ -2,8 +2,9 @@ import { Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { Tracer } from '@aws-lambda-powertools/tracer'; -// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor const tracer = new Tracer({ serviceName: 'tracerManualFn' }); +// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable +// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html export const handler = async (event: typeof Events.Custom.CustomEvent, context: Context): Promise => { const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) diff --git a/examples/cdk/lib/example-function.Tracer.Middleware.ts b/examples/cdk/lib/example-function.Tracer.Middleware.ts index 77ad9deb2c..810c285ca8 100644 --- a/examples/cdk/lib/example-function.Tracer.Middleware.ts +++ b/examples/cdk/lib/example-function.Tracer.Middleware.ts @@ -3,15 +3,15 @@ import { Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer'; -// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor const tracer = new Tracer({ serviceName: 'tracerMiddlewareFn' }); +// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable +// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html -// We instrument the handler with the middy middleware and the tracer will automatically create a subsegment and capture relevant annotations and metadata -export const handler = middy(async (event: typeof Events.Custom.CustomEvent, context: Context) => { +const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: Context) => { // Add custom annotation & metadata tracer.putAnnotation('awsRequestId', context.awsRequestId); tracer.putMetadata('eventPayload', event); - + let res; try { res = { foo: 'bar' }; @@ -20,4 +20,11 @@ export const handler = middy(async (event: typeof Events.Custom.CustomEvent, con } return res; -}).use(captureLambdaHandler(tracer)); \ No newline at end of file +} + +// We instrument the handler with the Middy middleware and the Tracer will automatically: +// * handle the lifecycle of the subsegment +// * create a ColdStart annotation to easily filter traces that have had an initialization overhead +// * create a Service annotation to easily filter traces that have a specific service name +// * captures any response, or full exceptions generated by the handler, and include them as tracing metadata +export const handler = middy(lambdaHandler).use(captureLambdaHandler(tracer)); \ No newline at end of file diff --git a/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv2.ts b/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv2.ts index a105d52c4a..08eab6c55c 100644 --- a/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv2.ts +++ b/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv2.ts @@ -3,11 +3,15 @@ import { Events } from '@aws-lambda-powertools/commons'; import { Tracer } from '@aws-lambda-powertools/tracer'; import { STS } from 'aws-sdk'; -// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor const tracer = new Tracer({ serviceName: 'tracerPatchAWSSDKv2Fn' }); -// To patch a specific AWS SDK Client, we pass it to the Tracer that will return an instrumented version of it +// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable +// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html + +// To patch a specific AWS SDK v2 Client, we pass it to the Tracer that will return an instrumented version of it const sts = tracer.captureAWSClient(new STS()); +// Here we are showing an example with manual instrumentation but you can do the same also with the captureLambdaHandler Middy Middleware and Class decorator +// See: https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/#lambda-handler export const handler = async (_event: typeof Events.Custom.CustomEvent, _context: Context): Promise => { const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) // Create subsegment for the function & set it as active @@ -18,16 +22,18 @@ export const handler = async (_event: typeof Events.Custom.CustomEvent, _context try { // Tracer will create a subsegment for the AWS SDK call and capture relevant annotations and metadata res = await sts.getCallerIdentity({}).promise(); - // Add custom metadata with the response object - tracer.putMetadata('awsResponse', res); + // Add the response as metadata + tracer.addResponseAsMetadata(res, process.env._HANDLER); } catch (err) { + // Add error as metadata to the current subsegment + tracer.addErrorAsMetadata(err as Error); throw err; + } finally { + // Close subsegment (the AWS Lambda one is closed automatically) + subsegment.close(); + // Set back the facade segment as active again + tracer.setSegment(segment); } - // Close subsegment (the AWS Lambda one is closed automatically) - subsegment.close(); - // Set the facade segment as active again - tracer.setSegment(segment); - return res; }; \ No newline at end of file diff --git a/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv3.ts b/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv3.ts index 1a9517a3a7..c53150f05a 100644 --- a/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv3.ts +++ b/examples/cdk/lib/example-function.Tracer.PatchAWSSDKv3.ts @@ -3,11 +3,15 @@ import { Events } from '@aws-lambda-powertools/commons'; import { Tracer } from '@aws-lambda-powertools/tracer'; import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts'; -// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor const tracer = new Tracer({ serviceName: 'tracerManualFn' }); +// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable +// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html + // To patch a specific AWS SDK v3 Client, we need to pass it to the Tracer that will return an instrumented version of it const sts = tracer.captureAWSv3Client(new STSClient({})); +// Here we are showing an example with manual instrumentation but you can do the same also with the captureLambdaHandler Middy Middleware and Class decorator +// See: https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/#lambda-handler export const handler = async (_event: typeof Events.Custom.CustomEvent, _context: Context): Promise => { const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) // Create subsegment for the function & set it as active @@ -18,16 +22,18 @@ export const handler = async (_event: typeof Events.Custom.CustomEvent, _context try { // Tracer will create a subsegment for the AWS SDK call and capture relevant annotations and metadata res = await sts.send(new GetCallerIdentityCommand({})); - // Add custom metadata with the response object - tracer.putMetadata('awsResponse', res); + // Add the response as metadata + tracer.addResponseAsMetadata(res, process.env._HANDLER); } catch (err) { + // Add error as metadata to the current subsegment + tracer.addErrorAsMetadata(err as Error); throw err; + } finally { + // Close subsegment (the AWS Lambda one is closed automatically) + subsegment.close(); + // Set back the facade segment as active again + tracer.setSegment(segment); } - // Close subsegment (the AWS Lambda one is closed automatically) - subsegment.close(); - // Set the facade segment as active again - tracer.setSegment(segment); - return res; }; \ No newline at end of file diff --git a/examples/cdk/lib/example-function.Tracer.PatchAllAWSSDK.ts b/examples/cdk/lib/example-function.Tracer.PatchAllAWSSDK.ts index d6fa8f6665..55fa5ba534 100644 --- a/examples/cdk/lib/example-function.Tracer.PatchAllAWSSDK.ts +++ b/examples/cdk/lib/example-function.Tracer.PatchAllAWSSDK.ts @@ -2,14 +2,18 @@ import { Context } from 'aws-lambda'; import { Events } from '@aws-lambda-powertools/commons'; import { Tracer } from '@aws-lambda-powertools/tracer'; -// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor const tracer = new Tracer({ serviceName: 'tracerPatchAllAWSSDKFn' }); -// To patch all AWS SDKs, we need to import aws-sdk and pass it to the Tracer +// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable +// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html + +// To patch all AWS SDKs v2, we need to import aws-sdk and pass it to the Tracer // eslint-disable-next-line @typescript-eslint/no-var-requires const AWS = tracer.captureAWS(require('aws-sdk')); // Then we can use the AWS SDK as usual const sts = new AWS.STS(); +// Here we are showing an example with manual instrumentation but you can do the same also with the captureLambdaHandler Middy Middleware and Class decorator +// See: https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/#lambda-handler export const handler = async (_event: typeof Events.Custom.CustomEvent, _context: Context): Promise => { const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda) // Create subsegment for the function & set it as active @@ -20,16 +24,18 @@ export const handler = async (_event: typeof Events.Custom.CustomEvent, _context try { // Tracer will create a subsegment for the AWS SDK call and capture relevant annotations and metadata res = await sts.getCallerIdentity({}).promise(); - // Add custom metadata with the response object - tracer.putMetadata('awsResponse', res); + // Add the response as metadata + tracer.addResponseAsMetadata(res, process.env._HANDLER); } catch (err) { + // Add error as metadata to the current subsegment + tracer.addErrorAsMetadata(err as Error); throw err; + } finally { + // Close subsegment (the AWS Lambda one is closed automatically) + subsegment.close(); + // Set back the facade segment as active again + tracer.setSegment(segment); } - // Close subsegment (the AWS Lambda one is closed automatically) - subsegment.close(); - // Set the facade segment as active again - tracer.setSegment(segment); - return res; }; \ No newline at end of file From 4ac474d6a5e76f00bff4c5d3c597d757c724b113 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 6 Jan 2022 23:50:48 +0100 Subject: [PATCH 2/5] fix: updated comment --- .../cdk/lib/example-function.MyFunctionWithMiddy.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts index 5c4a7529cc..555ae548c4 100644 --- a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts +++ b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts @@ -13,7 +13,9 @@ const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName }); const tracer = new Tracer({ serviceName: serviceName }); const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: Context) => { - // ### Experiment logger + // ### Experiment with Logger + // AWS Lambda context is automatically injected by the middleware + logger.addPersistentLogAttributes({ testKey: 'testValue', }); @@ -22,14 +24,16 @@ const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: C logger.warn('This is an WARN log'); logger.error('This is an ERROR log'); - // ### Experiment metrics + // ### Experiment with Metrics + // Default metrics, cold start, and throwOnEmptyMetrics are enabled by the middleware + metrics.addMetric('test-metric', MetricUnits.Count, 10); const metricWithItsOwnDimensions = metrics.singleMetric(); metricWithItsOwnDimensions.addDimension('InnerDimension', 'true'); metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50); - // ### Experiment tracer + // ### Experiment with Tracer // Service & Cold Start annotations will be added for you by the decorator/middleware From 5e033c61dc2949b523f1b1e8a1378c2d7f43b9b0 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 7 Jan 2022 09:29:26 +0100 Subject: [PATCH 3/5] fix: Update examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts Co-authored-by: ijemmy --- .../cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts b/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts index f96b5dd32d..6bdea71da0 100644 --- a/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts +++ b/examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts @@ -7,7 +7,7 @@ import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer'; process.env.POWERTOOLS_TRACER_ERROR_RESPONSE = 'false'; const tracer = new Tracer({ serviceName: 'tracerCaptureErrorDisabledFn' }); -// In this example we are using the middy middleware pattern but you can instrument your functions also with the captureLambdaHandler decorator & manual instrumentation +// In this example we are using the Middy middleware pattern but you can instrument your functions also with the captureLambdaHandler decorator & manual instrumentation export const handler = middy(async (event: typeof Events.Custom.CustomEvent, context: Context) => { tracer.putAnnotation('awsRequestId', context.awsRequestId); tracer.putMetadata('eventPayload', event); From 13c610333dbb9bcc3086788bae99b6377b22bfd3 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 7 Jan 2022 09:33:28 +0100 Subject: [PATCH 4/5] fix: Update examples/cdk/lib/example-function.MyFunctionWithMiddy.ts Co-authored-by: ijemmy --- examples/cdk/lib/example-function.MyFunctionWithMiddy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts index 555ae548c4..14e139fcfa 100644 --- a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts +++ b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts @@ -19,7 +19,7 @@ const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: C logger.addPersistentLogAttributes({ testKey: 'testValue', }); - logger.debug('This is an DEBUG log'); // Won't show by default + logger.debug('This is an DEBUG log'); // Won't show because we pass logLevel: 'INFO' in the constructor. logger.info('This is an INFO log'); logger.warn('This is an WARN log'); logger.error('This is an ERROR log'); From 9c2a4189f9cfc22a24b10e10d68ba2b4108d2117 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 7 Jan 2022 11:18:22 +0100 Subject: [PATCH 5/5] fix: added missing throw error statement --- examples/cdk/lib/example-function.MyFunction.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/cdk/lib/example-function.MyFunction.ts b/examples/cdk/lib/example-function.MyFunction.ts index 560ca6724d..1cad15a5af 100644 --- a/examples/cdk/lib/example-function.MyFunction.ts +++ b/examples/cdk/lib/example-function.MyFunction.ts @@ -61,6 +61,7 @@ export const handler = async (event: unknown, context: Context): Promise = } catch (err) { // Add the error as metadata subsegment.addError(err as Error, false); + throw err; } finally { // Close subsegments (the AWS Lambda one is closed automatically) subsegment.close(); // (### MySubSegment)