diff --git a/CHANGELOG.md b/CHANGELOG.md index b055574bb6..fa3ddaa650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v0.3.1...v0.3.2) (2022-01-17) + + +### Bug Fixes + +* export LogFormatter + update docs ([#479](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/479)) ([7f91566](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/7f91566d4ff34887914009e2424df7c39a96cd71)) +* updated CDK examples to remove old references & improve comments ([#439](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/439)) ([4cdaaea](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/4cdaaeaf7fbb24571b194c0e82338fbd216d2dcd)) + + + + + ## [0.3.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v0.3.0...v0.3.1) (2022-01-14) diff --git a/docs/core/logger.md b/docs/core/logger.md index 3193357f6a..bb7b3dd432 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -625,13 +625,13 @@ This is how the `MyCompanyLogFormatter` (dummy name) would look like: === "utils/formatters/MyCompanyLogFormatter.ts" ```typescript - import { LogFormatter } from "@aws-lambda-powertools/logger"; - import { LogAttributes, UnformattedAttributes } from "@aws-lambda-powertools/logger/types"; + import { LogFormatter } from '@aws-lambda-powertools/logger'; + import { LogAttributes, UnformattedAttributes } from '@aws-lambda-powertools/logger/lib/types'; // Replace this line with your own type type MyCompanyLog = LogAttributes; - class MyCompanyLogFormatter implements LogFormatter { + class MyCompanyLogFormatter extends LogFormatter { public formatAttributes(attributes: UnformattedAttributes): MyCompanyLog { return { diff --git a/examples/cdk/README.md b/examples/cdk/README.md index 3247665185..c6bcbbbe3c 100644 --- a/examples/cdk/README.md +++ b/examples/cdk/README.md @@ -1,14 +1,24 @@ -# Welcome to your CDK TypeScript project! +# AWS Lambda Powertools (TypeScript) examples in CDK -This is a blank project for TypeScript development with CDK. +This is a deployable CDK app that deploys AWS Lambda functions as part of a CloudFormation stack. These Lambda functions use the utilities made available as part of AWS Lambda Powertools (TypeScript) to demonstrate their usage. -The `cdk.json` file tells the CDK Toolkit how to execute your app. +You will need to have a valid AWS Account in order to deploy these resources. These resources may incur costs to your AWS Account. The cost from **some services** are covered by the [AWS Free Tier](https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all) but not all of them. If you don't have an AWS Account follow [these instructions to create one](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/). -## Useful commands +The example functions, located in the `lib` folder, are invoked automatically, twice, when deployed using the CDK construct defined in `lib/example-function.ts`. The first invocation demonstrates the effect on logs/metrics/annotations when the Lambda function has a cold start, and the latter without a cold start. - * `npm run build` compile typescript to js - * `npm run watch` watch for changes and compile - * `npm run test` perform the jest unit tests - * `cdk deploy` deploy this stack to your default AWS account/region - * `cdk diff` compare deployed stack with current state - * `cdk synth` emits the synthesized CloudFormation template +## Deploying the stack + + * Ensure that CDK v2 is installed globally on your machine (if not, run `npm install -g aws-cdk`) + * Navigate to this location of the repo in your terminal (`examples/cdk`) + * `npm install` + * `cdk deploy --all --profile ` + +Note: Prior to deploying you may need to run `cdk bootstrap aws:/// --profile ` if you have not already bootstrapped your account for CDK. + +## Viewing Utility Outputs + +All utility outputs can be viewed in the Amazon CloudWatch console. + + * `Logger` output can be found in Logs > Log groups + * `Metrics` output can be found in Metrics > All metrics > CdkExample + * `Tracer` output can be found in X-Ray traces > Traces diff --git a/examples/cdk/bin/cdk-app.ts b/examples/cdk/bin/cdk-app.ts index bbcff99e06..9d8060953b 100644 --- a/examples/cdk/bin/cdk-app.ts +++ b/examples/cdk/bin/cdk-app.ts @@ -4,4 +4,4 @@ import * as cdk from 'aws-cdk-lib'; import { CdkAppStack } from '../lib/example-stack'; const app = new cdk.App(); -new CdkAppStack(app, 'CdkAppStack', {}); \ No newline at end of file +new CdkAppStack(app, 'LambdaPowertoolsTypeScript-ExamplesCdkStack', {}); \ No newline at end of file diff --git a/examples/cdk/lib/example-function.MyFunction.ts b/examples/cdk/lib/example-function.MyFunction.ts index 05a1c78b5d..f8bf0c8caf 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.throwOnEmptyMetrics(); metrics.setDefaultDimensions({ environment: 'example', type: 'standardFunction' }); @@ -38,24 +43,31 @@ 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); + throw err; + } 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 35cede722c..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, 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..14e139fcfa 100644 --- a/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts +++ b/examples/cdk/lib/example-function.MyFunctionWithMiddy.ts @@ -1,43 +1,73 @@ 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 with Logger + // AWS Lambda context is automatically injected by the middleware - @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'); - } - } -} + logger.addPersistentLogAttributes({ + testKey: 'testValue', + }); + 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'); -const handler = middy(async (_event, _context) => { + // ### Experiment with Metrics + // Default metrics, cold start, and throwOnEmptyMetrics are enabled by the middleware - const handlerClass = new MyFunctionWithDecorator(); + metrics.addMetric('test-metric', MetricUnits.Count, 10); - return handlerClass.handler(_event, _context, () => console.log('Lambda invoked!')); -}); + const metricWithItsOwnDimensions = metrics.singleMetric(); + metricWithItsOwnDimensions.addDimension('InnerDimension', 'true'); + metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50); + + // ### Experiment with Tracer -handler.before(async (_request) => { - metrics.addMetric('beforeHandlerCalled', MetricUnits.Count, 1); -}); + // Service & Cold Start annotations will be added for you by the decorator/middleware -handler.after(async (_request) => { - // Won't be flushed since happens after - metrics.addMetric('afterHandlerCalled', MetricUnits.Count, 1); + // 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); -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..6bdea71da0 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 diff --git a/lerna.json b/lerna.json index 5ce03a4662..b349d0cdca 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.3.1", + "version": "0.3.2", "npmClient": "npm", "message": "chore(release): %s [skip ci]" } diff --git a/packages/logger/CHANGELOG.md b/packages/logger/CHANGELOG.md index ac48ccafbb..37caab1c36 100644 --- a/packages/logger/CHANGELOG.md +++ b/packages/logger/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v0.3.1...v0.3.2) (2022-01-17) + + +### Bug Fixes + +* export LogFormatter + update docs ([#479](https://github.com/awslabs/aws-lambda-powertools-typescript/issues/479)) ([7f91566](https://github.com/awslabs/aws-lambda-powertools-typescript/commit/7f91566d4ff34887914009e2424df7c39a96cd71)) + + + + + ## [0.3.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v0.3.0...v0.3.1) (2022-01-14) diff --git a/packages/logger/package-lock.json b/packages/logger/package-lock.json index e2ab0dd93d..5eaeaf9903 100644 --- a/packages/logger/package-lock.json +++ b/packages/logger/package-lock.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/logger", - "version": "0.3.1", + "version": "0.3.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/logger/package.json b/packages/logger/package.json index 9b4891db27..3f5bd15d4f 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/logger", - "version": "0.3.1", + "version": "0.3.2", "description": "The logging package for the AWS Lambda powertools (TypeScript) library", "author": { "name": "Amazon Web Services", diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index d56574a719..577d19f6d5 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,3 +1,4 @@ export * from './helpers'; export * from './Logger'; -export * from './middleware'; \ No newline at end of file +export * from './middleware'; +export * from './formatter'; \ No newline at end of file diff --git a/packages/metrics/CHANGELOG.md b/packages/metrics/CHANGELOG.md index d40b1bb0d6..c30363b89d 100644 --- a/packages/metrics/CHANGELOG.md +++ b/packages/metrics/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.2](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v0.3.1...v0.3.2) (2022-01-17) + +**Note:** Version bump only for package @aws-lambda-powertools/metrics + + + + + ## [0.3.1](https://github.com/awslabs/aws-lambda-powertools-typescript/compare/v0.3.0...v0.3.1) (2022-01-14) diff --git a/packages/metrics/package-lock.json b/packages/metrics/package-lock.json index b3be8a93d8..edf6f2e2ee 100644 --- a/packages/metrics/package-lock.json +++ b/packages/metrics/package-lock.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/metrics", - "version": "0.3.1", + "version": "0.3.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 0ca0116eed..e3024e380a 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,6 +1,6 @@ { "name": "@aws-lambda-powertools/metrics", - "version": "0.3.1", + "version": "0.3.2", "description": "The metrics package for the AWS Lambda powertools (TypeScript) library", "author": { "name": "Amazon Web Services", diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index 3afb21a6f4..764d840b10 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -210,7 +210,7 @@ class Metrics implements MetricsInterface { * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; * import { Callback, Context } from 'aws-lambda'; * - * const metrics = new Metrics({namespace:"CDKExample", serviceName:"withDecorator"}); + * const metrics = new Metrics({namespace:"CdkExample", serviceName:"withDecorator"}); * * export class MyFunctionWithDecorator { * @@ -267,7 +267,7 @@ class Metrics implements MetricsInterface { * ```typescript * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; * - * const metrics = new Metrics({namespace: "CDKExample", serviceName: "MyFunction"}); // Sets metric namespace, and service as a metric dimension + * const metrics = new Metrics({namespace: "CdkExample", serviceName: "MyFunction"}); // Sets metric namespace, and service as a metric dimension * * export const handler = async (_event: any, _context: any) => { * metrics.addMetric('test-metric', MetricUnits.Count, 10); diff --git a/packages/metrics/tests/e2e/decorator.test.MyFunction.ts b/packages/metrics/tests/e2e/decorator.test.MyFunction.ts index 1c8fe06209..60c007c040 100644 --- a/packages/metrics/tests/e2e/decorator.test.MyFunction.ts +++ b/packages/metrics/tests/e2e/decorator.test.MyFunction.ts @@ -2,7 +2,7 @@ import { Metrics, MetricUnits } from '../../src'; import { Context } from 'aws-lambda'; import { LambdaInterface } from '../../examples/utils/lambda/LambdaInterface'; -const namespace = process.env.EXPECTED_NAMESPACE ?? 'CDKExample'; +const namespace = process.env.EXPECTED_NAMESPACE ?? 'CdkExample'; const serviceName = process.env.EXPECTED_SERVICE_NAME ?? 'MyFunctionWithStandardHandler'; const metricName = process.env.EXPECTED_METRIC_NAME ?? 'MyMetric'; const metricUnit = (process.env.EXPECTED_METRIC_UNIT as MetricUnits) ?? MetricUnits.Count; diff --git a/packages/metrics/tests/e2e/standardFunctions.test.MyFunction.ts b/packages/metrics/tests/e2e/standardFunctions.test.MyFunction.ts index 732ab872b4..997b5775d9 100644 --- a/packages/metrics/tests/e2e/standardFunctions.test.MyFunction.ts +++ b/packages/metrics/tests/e2e/standardFunctions.test.MyFunction.ts @@ -1,7 +1,7 @@ import { Metrics, MetricUnits } from '../../src'; import { Context } from 'aws-lambda'; -const namespace = process.env.EXPECTED_NAMESPACE ?? 'CDKExample'; +const namespace = process.env.EXPECTED_NAMESPACE ?? 'CdkExample'; const serviceName = process.env.EXPECTED_SERVICE_NAME ?? 'MyFunctionWithStandardHandler'; const metricName = process.env.EXPECTED_METRIC_NAME ?? 'MyMetric'; const metricUnit = (process.env.EXPECTED_METRIC_UNIT as MetricUnits) ?? MetricUnits.Count;