Skip to content

Commit f0f9309

Browse files
author
Michael Brewer
committed
Merge branch 'main' into fix-doc-examples
2 parents e2f2f7c + 646a9e8 commit f0f9309

19 files changed

+217
-127
lines changed

docs/core/logger.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Key | Example
112112

113113
logger.addContext(context);
114114
115-
logger.info("This is an INFO log with some context");
115+
logger.info('This is an INFO log with some context');
116116

117117
};
118118
```
@@ -395,7 +395,7 @@ This can be useful for example if you want to enable multiple Loggers with diffe
395395
});
396396

397397
// With this logger, only the ERROR logs will be printed
398-
const childLogger = parentLogger.createChild({
398+
const childLogger = logger.createChild({
399399
logLevel: 'ERROR'
400400
});
401401

@@ -606,10 +606,10 @@ You can customize the structure (keys and values) of your log items by passing a
606606
},
607607
});
608608

609-
export const handler = async (event, _context): Promise<void> => {
609+
export const handler = async (event, context): Promise<void> => {
610610

611611
logger.addContext(context);
612-
612+
613613
logger.info('This is an INFO log', { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } });
614614

615615
};
@@ -620,7 +620,7 @@ This is how the `MyCompanyLogFormatter` (dummy name) would look like:
620620
=== "utils/formatters/MyCompanyLogFormatter.ts"
621621

622622
```typescript
623-
import { LogFormatter } from '@aws-lambda-powertools/logger/lib/formatter';
623+
import { LogFormatter } from '@aws-lambda-powertools/logger';
624624
import { LogAttributes, UnformattedAttributes } from '@aws-lambda-powertools/logger/lib/types';
625625

626626
// Replace this line with your own type

examples/cdk/README.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
# Welcome to your CDK TypeScript project!
1+
# AWS Lambda Powertools (TypeScript) examples in CDK
22

3-
This is a blank project for TypeScript development with CDK.
3+
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.
44

5-
The `cdk.json` file tells the CDK Toolkit how to execute your app.
5+
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/).
66

7-
## Useful commands
7+
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.
88

9-
* `npm run build` compile typescript to js
10-
* `npm run watch` watch for changes and compile
11-
* `npm run test` perform the jest unit tests
12-
* `cdk deploy` deploy this stack to your default AWS account/region
13-
* `cdk diff` compare deployed stack with current state
14-
* `cdk synth` emits the synthesized CloudFormation template
9+
## Deploying the stack
10+
11+
* Ensure that CDK v2 is installed globally on your machine (if not, run `npm install -g aws-cdk`)
12+
* Navigate to this location of the repo in your terminal (`examples/cdk`)
13+
* `npm install`
14+
* `cdk deploy --all --profile <YOUR_AWS_PROFILE>`
15+
16+
Note: Prior to deploying you may need to run `cdk bootstrap aws://<YOU_AWS_ACCOUNT_ID>/<AWS_REGION> --profile <YOUR_AWS_PROFILE>` if you have not already bootstrapped your account for CDK.
17+
18+
## Viewing Utility Outputs
19+
20+
All utility outputs can be viewed in the Amazon CloudWatch console.
21+
22+
* `Logger` output can be found in Logs > Log groups
23+
* `Metrics` output can be found in Metrics > All metrics > CdkExample
24+
* `Tracer` output can be found in X-Ray traces > Traces

examples/cdk/bin/cdk-app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import * as cdk from 'aws-cdk-lib';
44
import { CdkAppStack } from '../lib/example-stack';
55

66
const app = new cdk.App();
7-
new CdkAppStack(app, 'CdkAppStack', {});
7+
new CdkAppStack(app, 'LambdaPowertoolsTypeScript-ExamplesCdkStack', {});

examples/cdk/lib/example-function.MyFunction.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ const metrics = new Metrics({ namespace: namespace, serviceName: serviceName });
1010
const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName });
1111
const tracer = new Tracer({ serviceName: serviceName });
1212

13-
export const handler = async (_event: unknown, context: Context): Promise<void> => {
13+
export const handler = async (event: unknown, context: Context): Promise<void> => {
1414
// 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)
1515
// we do it at the beginning because we want to trace the whole duration of the handler
16-
const segment = tracer.getSegment(); // This is the facade segment (the one that is created by Lambda & that can't be manipulated)
17-
const handlerSegment = segment.addNewSubsegment(`## ${context.functionName}`);
18-
// TODO: expose tracer.annotateColdStart()
19-
tracer.putAnnotation('ColdStart', Tracer.coldStart);
16+
const segment = tracer.getSegment(); // This is the facade segment (the one that is created by AWS Lambda)
17+
// Create subsegment for the function & set it as active
18+
const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
19+
tracer.setSegment(handlerSegment);
2020

21-
// ### Experiment logger
21+
// Annotate the subsegment with the cold start & serviceName
22+
tracer.annotateColdStart();
23+
tracer.addServiceNameAnnotation();
24+
25+
// ### Experiment with Logger
26+
logger.addContext(context);
2227
logger.addPersistentLogAttributes({
2328
testKey: 'testValue',
2429
});
@@ -27,7 +32,7 @@ export const handler = async (_event: unknown, context: Context): Promise<void>
2732
logger.warn('This is an WARN log');
2833
logger.error('This is an ERROR log');
2934

30-
// ### Experiment metrics
35+
// ### Experiment with Metrics
3136
metrics.captureColdStartMetric();
3237
metrics.throwOnEmptyMetrics();
3338
metrics.setDefaultDimensions({ environment: 'example', type: 'standardFunction' });
@@ -40,22 +45,29 @@ export const handler = async (_event: unknown, context: Context): Promise<void>
4045
metrics.publishStoredMetrics();
4146
metrics.throwOnEmptyMetrics();
4247

43-
// ### Experiment tracer
44-
45-
tracer.putAnnotation('Myannotation', 'My annotation\'s value');
48+
// ### Experiment with Tracer
49+
// This annotation & metadata will be added to the handlerSegment subsegment (## index.handler)
50+
tracer.putAnnotation('awsRequestId', context.awsRequestId);
51+
tracer.putMetadata('eventPayload', event);
4652

47-
// Create subsegment & set it as active
48-
const subsegment = handlerSegment.addNewSubsegment('MySubSegment');
53+
// Create another subsegment & set it as active
54+
const subsegment = handlerSegment.addNewSubsegment('### MySubSegment');
55+
tracer.setSegment(subsegment);
4956

57+
let res;
5058
try {
51-
throw new Error('test');
52-
// Add the response as metadata
59+
res = { foo: 'bar' };
60+
tracer.addResponseAsMetadata(res, process.env._HANDLER);
5361
} catch (err) {
5462
// Add the error as metadata
5563
subsegment.addError(err as Error, false);
64+
throw err;
65+
} finally {
66+
// Close subsegments (the AWS Lambda one is closed automatically)
67+
subsegment.close(); // (### MySubSegment)
68+
handlerSegment.close(); // (## index.handler)
69+
// Set the facade segment as active again (the one created by AWS Lambda)
70+
tracer.setSegment(segment);
5671
}
5772

58-
// Close subsegment
59-
subsegment.close();
60-
handlerSegment.close();
6173
};
Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Tracer } from '@aws-lambda-powertools/tracer';
2-
import { Callback, Context } from 'aws-lambda';
2+
import { Context } from 'aws-lambda';
3+
import { Events, LambdaInterface } from '@aws-lambda-powertools/commons';
34
import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics';
45
import { Logger } from '@aws-lambda-powertools/logger';
56

@@ -10,15 +11,16 @@ const metrics = new Metrics({ namespace: namespace, serviceName: serviceName });
1011
const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName });
1112
const tracer = new Tracer({ serviceName: serviceName });
1213

13-
export class MyFunctionWithDecorator {
14+
export class MyFunctionWithDecorator implements LambdaInterface {
15+
// We decorate the handler with the various decorators
1416
@tracer.captureLambdaHandler()
1517
@logger.injectLambdaContext()
1618
@metrics.logMetrics({
1719
captureColdStartMetric: true,
1820
throwOnEmptyMetrics: true,
1921
defaultDimensions: { environment: 'example', type: 'withDecorator' },
2022
})
21-
public handler(_event: unknown, _context: Context, _callback: Callback<unknown>): void | Promise<unknown> {
23+
public async handler(event: typeof Events.Custom.CustomEvent, context: Context): Promise<unknown> {
2224
// ### Experiment logger
2325
logger.addPersistentLogAttributes({
2426
testKey: 'testValue',
@@ -36,28 +38,34 @@ export class MyFunctionWithDecorator {
3638
metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50);
3739

3840
// ### Experiment tracer
39-
tracer.putAnnotation('Myannotation', 'My annotation\'s value');
4041

41-
// Create subsegment & set it as active
42-
const segment = tracer.getSegment(); // This is the facade segment (the one that is created by Lambda & that can't be manipulated)
43-
const subsegment = segment.addNewSubsegment('MySubSegment');
42+
// Service & Cold Start annotations will be added for you by the decorator/middleware
4443

44+
// These traces will be added to the main segment (## index.handler)
45+
tracer.putAnnotation('awsRequestId', context.awsRequestId);
46+
tracer.putMetadata('eventPayload', event);
47+
48+
// Create another subsegment & set it as active
49+
const handlerSegment = tracer.getSegment(); // This is the custom segment created by Tracer for you (## index.handler)
50+
const subsegment = handlerSegment.addNewSubsegment('### MySubSegment');
4551
tracer.setSegment(subsegment);
46-
// TODO: Add the ColdStart annotation !!! NOT POSSIBLE
47-
// tracer.putAnnotation('ColdStart', tracer);
4852

53+
let res;
4954
try {
50-
throw new Error('test');
51-
// Add the response as metadata
55+
res = { foo: 'bar' };
5256
} catch (err) {
53-
// Add the error as metadata
54-
subsegment.addError(err as Error, false);
57+
throw err;
58+
} finally {
59+
// Close the subsegment you created (### MySubSegment)
60+
subsegment.close();
61+
// Set back the original segment as active (## index.handler)
62+
tracer.setSegment(handlerSegment);
63+
// The main segment (facade) will be closed for you at the end by the decorator/middleware
5564
}
56-
57-
// Close subsegment
58-
subsegment.close();
65+
66+
return res;
5967
}
6068
}
6169

62-
export const handlerClass = new MyFunctionWithDecorator();
63-
export const handler = handlerClass.handler;
70+
export const myFunction = new MyFunctionWithDecorator();
71+
export const handler = myFunction.handler;
Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,73 @@
11
import middy from '@middy/core';
2-
import { Callback, Context } from 'aws-lambda';
3-
import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics';
2+
import { Context } from 'aws-lambda';
3+
import { Events } from '@aws-lambda-powertools/commons';
4+
import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics';
5+
import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer';
6+
import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger';
47

5-
const metrics = new Metrics({ namespace: 'CDKExample', serviceName: 'withMiddy' }); // Sets metric namespace, and service as a metric dimension
8+
const namespace = 'CDKExample';
9+
const serviceName = 'MyFunctionWithMiddyMiddleware';
610

7-
type CustomEvent = {
8-
throw: boolean
9-
};
11+
const metrics = new Metrics({ namespace: namespace, serviceName: serviceName });
12+
const logger = new Logger({ logLevel: 'INFO', serviceName: serviceName });
13+
const tracer = new Tracer({ serviceName: serviceName });
1014

11-
class MyFunctionWithDecorator {
15+
const lambdaHandler = async (event: typeof Events.Custom.CustomEvent, context: Context) => {
16+
// ### Experiment with Logger
17+
// AWS Lambda context is automatically injected by the middleware
1218

13-
@metrics.logMetrics({ captureColdStartMetric: true })
14-
public handler(_event: CustomEvent, _context: Context, _callback: Callback<unknown>): void | Promise<unknown> {
15-
metrics.addMetric('test-metric', MetricUnits.Count, 10);
16-
if (_event.throw) {
17-
throw new Error('Test error');
18-
}
19-
}
20-
}
19+
logger.addPersistentLogAttributes({
20+
testKey: 'testValue',
21+
});
22+
logger.debug('This is an DEBUG log'); // Won't show because we pass logLevel: 'INFO' in the constructor.
23+
logger.info('This is an INFO log');
24+
logger.warn('This is an WARN log');
25+
logger.error('This is an ERROR log');
2126

22-
const handler = middy(async (_event, _context) => {
27+
// ### Experiment with Metrics
28+
// Default metrics, cold start, and throwOnEmptyMetrics are enabled by the middleware
2329

24-
const handlerClass = new MyFunctionWithDecorator();
30+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
2531

26-
return handlerClass.handler(_event, _context, () => console.log('Lambda invoked!'));
27-
});
32+
const metricWithItsOwnDimensions = metrics.singleMetric();
33+
metricWithItsOwnDimensions.addDimension('InnerDimension', 'true');
34+
metricWithItsOwnDimensions.addMetric('single-metric', MetricUnits.Percent, 50);
35+
36+
// ### Experiment with Tracer
2837

29-
handler.before(async (_request) => {
30-
metrics.addMetric('beforeHandlerCalled', MetricUnits.Count, 1);
31-
});
38+
// Service & Cold Start annotations will be added for you by the decorator/middleware
3239

33-
handler.after(async (_request) => {
34-
// Won't be flushed since happens after
35-
metrics.addMetric('afterHandlerCalled', MetricUnits.Count, 1);
40+
// These traces will be added to the main segment (## index.handler)
41+
tracer.putAnnotation('awsRequestId', context.awsRequestId);
42+
tracer.putMetadata('eventPayload', event);
3643

37-
});
44+
// Create another subsegment & set it as active
45+
const handlerSegment = tracer.getSegment(); // This is the custom segment created by Tracer for you (## index.handler)
46+
const subsegment = handlerSegment.addNewSubsegment('### MySubSegment');
47+
tracer.setSegment(subsegment);
3848

39-
handler.onError(async (_request) => {
40-
metrics.addMetric('onErrorHandlerCalled', MetricUnits.Count, 1);
41-
});
49+
let res;
50+
try {
51+
res = { foo: 'bar' };
52+
} catch (err) {
53+
throw err;
54+
} finally {
55+
// Close the subsegment you created (### MySubSegment)
56+
subsegment.close();
57+
// Set back the original segment as active (## index.handler)
58+
tracer.setSegment(handlerSegment);
59+
// The main segment (facade) will be closed for you at the end by the decorator/middleware
60+
}
61+
62+
return res;
63+
}
4264

43-
module.exports = { handler };
65+
// We instrument the handler with the various Middy middlewares
66+
export const handler = middy(lambdaHandler)
67+
.use(captureLambdaHandler(tracer))
68+
.use(logMetrics(metrics, {
69+
captureColdStartMetric: true,
70+
throwOnEmptyMetrics: true,
71+
defaultDimensions: { environment: 'example', type: 'withDecorator' },
72+
}))
73+
.use(injectLambdaContext(logger));

examples/cdk/lib/example-function.Tracer.CaptureErrorDisabled.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { Context } from 'aws-lambda';
33
import { Events } from '@aws-lambda-powertools/commons';
44
import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer';
55

6-
// Set environment variable to disable capture response
6+
// Set environment variable to disable capture response - https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
77
process.env.POWERTOOLS_TRACER_ERROR_RESPONSE = 'false';
88
const tracer = new Tracer({ serviceName: 'tracerCaptureErrorDisabledFn' });
99

10-
// In this example we are using the middleware pattern but you could use also the captureLambdaHandler decorator
10+
// In this example we are using the Middy middleware pattern but you can instrument your functions also with the captureLambdaHandler decorator & manual instrumentation
1111
export const handler = middy(async (event: typeof Events.Custom.CustomEvent, context: Context) => {
1212
tracer.putAnnotation('awsRequestId', context.awsRequestId);
1313
tracer.putMetadata('eventPayload', event);

examples/cdk/lib/example-function.Tracer.CaptureResponseDisabled.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Context } from 'aws-lambda';
33
import { Events } from '@aws-lambda-powertools/commons';
44
import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer';
55

6-
// Set environment variable to disable capture response
6+
// Set environment variable to disable capture response - https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
77
process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false';
88
const tracer = new Tracer({ serviceName: 'tracerCaptureResponseDisabledFn' });
99

examples/cdk/lib/example-function.Tracer.Decorator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { Callback, Context } from 'aws-lambda';
22
import { Events } from '@aws-lambda-powertools/commons';
33
import { Tracer } from '@aws-lambda-powertools/tracer';
44

5-
// process.env.POWERTOOLS_SERVICE_NAME = 'tracerManualFn'; // Alternative to setting the service name in the constructor
65
const tracer = new Tracer({ serviceName: 'tracerDecoratorFn' });
6+
// Alternatively, you can also set the service name using the POWERTOOLS_SERVICE_NAME environment variable
7+
// Learn more at: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
78

89
export class MyFunctionWithDecorator {
910
// We instrument the handler with the decorator and the tracer will automatically create a subsegment and capture relevant annotations and metadata

0 commit comments

Comments
 (0)