Skip to content

Commit 18f1097

Browse files
committed
feat(logger): add context decorator functionality (#13)
* refactor(logger): Logger class * test(logger): set coverage 100 * test(logger): disabled coverage check temporarily * feat(logger): add context decorator functionality * docs(logger): Lambda first letter uppercase * fix(logger): remove duplicate import line * chore(logger): Lambda first letter uppercase * test(all): hide implementation detail of tests in package
1 parent bb2635e commit 18f1097

13 files changed

+163
-11
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"commit": "commit",
99
"lerna-ci": "lerna exec -- npm ci",
10-
"lerna-test": "lerna exec -- jest --coverage --detectOpenHandles",
10+
"lerna-test": "lerna exec -- npm run test",
1111
"lerna-build": "lerna exec -- tsc",
1212
"lerna-lint": "lerna exec -- eslint \"./{src,tests}/**/*.ts\"",
1313
"lerna-format": "lerna exec -- eslint --fix \"./{src,tests}/**/*.ts\"",

packages/logger/README.md

+47
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ logger.error('This is an ERROR log');
5050

5151
### Capturing Lambda context info
5252

53+
Without decorators:
54+
5355
```typescript
5456
// Environment variables set for the Lambda
5557
process.env.LOG_LEVEL = 'WARN';
@@ -107,6 +109,51 @@ const lambdaHandler: Handler = async (event, context) => {
107109
</details>
108110

109111

112+
With decorators:
113+
114+
```typescript
115+
// Environment variables set for the Lambda
116+
process.env.LOG_LEVEL = 'INFO';
117+
process.env.POWERTOOLS_SERVICE_NAME = 'hello-world';
118+
119+
const logger = new Logger();
120+
121+
class Lambda implements LambdaInterface {
122+
123+
@logger.injectLambdaContext()
124+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
125+
126+
logger.info('This is an INFO log with some context');
127+
128+
}
129+
130+
}
131+
132+
new Lambda().handler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));
133+
134+
```
135+
136+
<details>
137+
<summary>Click to expand and see the logs outputs</summary>
138+
139+
```bash
140+
141+
{
142+
aws_request_id: 'c6af9ac6-7b61-11e6-9a41-93e8deadbeef',
143+
lambda_function_arn: 'arn:aws:lambda:eu-central-1:123456789012:function:Example',
144+
lambda_function_memory_size: 128,
145+
lambda_function_name: 'foo-bar-function',
146+
level: 'INFO',
147+
message: 'This is an INFO log with some context',
148+
service: 'hello-world',
149+
timestamp: '2021-03-17T08:25:41.198Z',
150+
xray_trace_id: 'abcdef123456abcdef123456abcdef123456'
151+
}
152+
153+
```
154+
</details>
155+
156+
110157
### Appending additional keys
111158

112159
```typescript

packages/logger/examples/child-logger.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ const lambdaHandler: Handler = async () => {
3030

3131
};
3232

33-
lambdaHandler(dummyEvent, dummyContext, () => {});
33+
lambdaHandler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { populateEnvironmentVariables } from '../tests/helpers';
2+
3+
// Populate runtime
4+
populateEnvironmentVariables();
5+
// Additional runtime variables
6+
process.env.LOG_LEVEL = 'INFO';
7+
process.env.POWERTOOLS_SERVICE_NAME = 'hello-world';
8+
9+
import * as dummyEvent from '../../../tests/resources/events/custom/hello-world.json';
10+
import { context as dummyContext } from '../../../tests/resources/contexts/hello-world';
11+
import { LambdaInterface } from '../src/lambda/LambdaInterface';
12+
import { Logger } from '../src';
13+
import { Callback, Context } from 'aws-lambda/handler';
14+
15+
const logger = new Logger();
16+
17+
class Lambda implements LambdaInterface {
18+
19+
@logger.injectLambdaContext()
20+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
21+
22+
logger.info('This is an INFO log with some context');
23+
24+
}
25+
26+
}
27+
28+
new Lambda().handler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));

packages/logger/examples/sample-rate.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ const lambdaHandler: Handler = async () => {
3030

3131
};
3232

33-
lambdaHandler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));
33+
lambdaHandler(dummyEvent, dummyContext, () => console.log('lambda invoked!'));

packages/logger/jest.config.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ module.exports = {
2222
],
2323
'coverageThreshold': {
2424
'global': {
25-
'statements': 70,
26-
'branches': 60,
27-
'functions': 70,
28-
'lines': 70,
25+
'statements': 100,
26+
'branches': 100,
27+
'functions': 100,
28+
'lines': 100,
2929
},
3030
},
3131
'coverageReporters': [

packages/logger/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"scripts": {
1010
"commit": "commit",
11-
"test": "jest --coverage --detectOpenHandles",
11+
"test": "jest --detectOpenHandles",
1212
"watch": "jest --watch",
1313
"build": "tsc",
1414
"lint": "eslint \"./{src,tests}/**/*.ts\"",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Handler } from 'aws-lambda';
2+
3+
interface LambdaInterface {
4+
handler: Handler
5+
}
6+
7+
export {
8+
LambdaInterface
9+
};

packages/logger/src/lambda/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './LambdaInterface';

packages/logger/tests/unit/Logger.test.ts

+67-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { context as dummyContext } from '../../../../tests/resources/contexts/hello-world';
12
import { Logger } from '../../src';
23
import { populateEnvironmentVariables } from '../helpers';
34

@@ -55,8 +56,13 @@ describe('Logger', () => {
5556

5657
logger.error('foo');
5758
logger.error('foo', { bar: 'baz' });
59+
logger.error({ bar: 'baz', message: 'foo' });
5860

59-
expect(console.log).toBeCalledTimes(2);
61+
const error = new Error('Something happened!');
62+
error.stack = 'A custom stack trace';
63+
logger.error('foo', { bar: 'baz' }, error);
64+
65+
expect(console.log).toBeCalledTimes(4);
6066
expect(console.log).toHaveBeenNthCalledWith(1, {
6167
message: 'foo',
6268
service: 'hello-world',
@@ -72,6 +78,27 @@ describe('Logger', () => {
7278
timestamp: '2016-06-20T12:08:10.000Z',
7379
xray_trace_id: 'abcdef123456abcdef123456abcdef123456'
7480
});
81+
expect(console.log).toHaveBeenNthCalledWith(3, {
82+
bar: 'baz',
83+
message: 'foo',
84+
service: 'hello-world',
85+
level: 'ERROR',
86+
timestamp: '2016-06-20T12:08:10.000Z',
87+
xray_trace_id: 'abcdef123456abcdef123456abcdef123456'
88+
});
89+
expect(console.log).toHaveBeenNthCalledWith(4, {
90+
bar: 'baz',
91+
error: {
92+
message: 'Something happened!',
93+
name: 'Error',
94+
stack: 'A custom stack trace',
95+
},
96+
message: 'foo',
97+
service: 'hello-world',
98+
level: 'ERROR',
99+
timestamp: '2016-06-20T12:08:10.000Z',
100+
xray_trace_id: 'abcdef123456abcdef123456abcdef123456'
101+
});
75102
});
76103

77104
test('should return a valid DEBUG log', () => {
@@ -125,5 +152,44 @@ describe('Logger', () => {
125152

126153
});
127154

155+
test('should return a valid INFO log with context enabled', () => {
156+
157+
const logger = new Logger({
158+
isContextEnabled: true
159+
});
160+
logger.addContext(dummyContext);
161+
162+
logger.info('foo');
163+
logger.info( { message: 'foo', bar: 'baz' });
164+
165+
expect(console.log).toBeCalledTimes(2);
166+
expect(console.log).toHaveBeenNthCalledWith(1, {
167+
'aws_request_id': 'c6af9ac6-7b61-11e6-9a41-93e8deadbeef',
168+
'cold_start': true,
169+
'lambda_function_arn': 'arn:aws:lambda:eu-central-1:123456789012:function:Example',
170+
'lambda_function_memory_size': 128,
171+
'lambda_function_name': 'foo-bar-function',
172+
'level': 'INFO',
173+
'message': 'foo',
174+
'service': 'hello-world',
175+
'timestamp': '2016-06-20T12:08:10.000Z',
176+
'xray_trace_id': 'abcdef123456abcdef123456abcdef123456'
177+
});
178+
expect(console.log).toHaveBeenNthCalledWith(2, {
179+
'aws_request_id': 'c6af9ac6-7b61-11e6-9a41-93e8deadbeef',
180+
'bar': 'baz',
181+
'cold_start': true,
182+
'lambda_function_arn': 'arn:aws:lambda:eu-central-1:123456789012:function:Example',
183+
'lambda_function_memory_size': 128,
184+
'lambda_function_name': 'foo-bar-function',
185+
'level': 'INFO',
186+
'message': 'foo',
187+
'service': 'hello-world',
188+
'timestamp': '2016-06-20T12:08:10.000Z',
189+
'xray_trace_id': 'abcdef123456abcdef123456abcdef123456'
190+
});
191+
192+
});
193+
128194
});
129195

packages/logger/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"compilerOptions": {
3+
"experimentalDecorators": true,
34
"noImplicitAny": true,
45
"target": "ES2020",
56
"module": "commonjs",

packages/logger/types/Log.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ type LogLevelInfo = 'INFO';
33
type LogLevelWarn = 'WARN';
44
type LogLevelError = 'ERROR';
55

6-
type LogLevel = LogLevelDebug | LogLevelInfo | LogLevelWarn | LogLevelError;
6+
type LogLevel = LogLevelDebug | LogLevelInfo | LogLevelWarn | LogLevelError | string;
77

88
type LogLevelThresholds = {
99
[key in LogLevel]: number;

packages/logger/types/formats/PowertoolLog.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LogAttributes, LogLevel } from '../Log';
1+
import { LogAttributes, LogLevel } from '..';
22

33
type PowertoolLog = LogAttributes & {
44

0 commit comments

Comments
 (0)