Skip to content

Commit 9551070

Browse files
sarageriondreamorosi
authored andcommitted
Feat(logger): log event functionality (#1004)
* feat(logger): log event, add business logic and unit tests * test(logger): add log event support * feat(logger): add env variable for log event * test(logger): middy * chore(logger): middy polish for log event * test(logger): fix log event unit tests * test(logger): unit + e2e tests for log event functionality * docs(logger): add log event * docs: add single line command for docs generation * docs: fix typo in env vars section
1 parent e3d031e commit 9551070

17 files changed

+508
-70
lines changed

CONTRIBUTING.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,11 @@ You can build and start the API reference website by running these two commands
238238

239239
#### Docs website
240240

241-
You can build and start a local docs website by running these two commands:
241+
You can build and start a local docs website by running:
242+
243+
`npm run docs-website-build-run`
244+
245+
Alternatively you can run these two commands:
242246

243247
* `npm run docs-buildDockerImage` OR `docker build -t squidfunk/mkdocs-material ./docs/`
244248
* `npm run docs-runLocalDocker` OR `docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material`

docs/core/logger.md

+47-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ Logger provides an opinionated logger with output structured as JSON.
1414

1515
## Key features
1616

17-
* Capture key fields from Lambda context, cold start and structures logging output as JSON
18-
* Log Lambda context when instructed (disabled by default)
19-
* Log sampling prints all logs for a percentage of invocations (disabled by default)
17+
* Capture key fields from Lambda context, cold start and structure logging output as JSON
18+
* Log Lambda event when instructed (disabled by default)
19+
* Log sampling prints all the logs for a percentage of invocations (disabled by default)
2020
* Append additional keys to structured log at any point in time
2121

2222
## Getting started
@@ -193,6 +193,50 @@ In each case, the printed log will look like this:
193193
}
194194
```
195195

196+
#### Log incoming event
197+
198+
When debugging in non-production environments, you can instruct Logger to log the incoming event with the middleware/decorator parameter `logEvent` or via POWERTOOLS_LOGGER_LOG_EVENT env var set to `true`.
199+
200+
???+ warning
201+
This is disabled by default to prevent sensitive info being logged
202+
203+
=== "Middy Middleware"
204+
205+
```typescript hl_lines="11"
206+
import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger';
207+
import middy from '@middy/core';
208+
209+
const logger = new Logger();
210+
211+
const lambdaHandler = async (_event: any, _context: any): Promise<void> => {
212+
logger.info('This is an INFO log with some context');
213+
};
214+
215+
export const handler = middy(lambdaHandler)
216+
.use(injectLambdaContext(logger, { logEvent: true }));
217+
```
218+
219+
=== "Decorator"
220+
221+
```typescript hl_lines="8"
222+
import { Logger } from '@aws-lambda-powertools/logger';
223+
import { LambdaInterface } from '@aws-lambda-powertools/commons';
224+
225+
const logger = new Logger();
226+
227+
class Lambda implements LambdaInterface {
228+
// Set the log event flag to true
229+
@logger.injectLambdaContext({ logEvent: true })
230+
public async handler(_event: any, _context: any): Promise<void> {
231+
logger.info('This is an INFO log with some context');
232+
}
233+
234+
}
235+
236+
export const myFunction = new Lambda();
237+
export const handler = myFunction.handler;
238+
```
239+
196240
### Appending persistent additional log keys and values
197241

198242
You can append additional persistent keys and values in the logs generated during a Lambda invocation using either mechanism:

docs/index.md

+14-14
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,20 @@ Each TypeScript utility is installed as standalone NPM package.
4545
## Environment variables
4646

4747
!!! info
48-
**Explicit parameters take precedence over environment variables.**
49-
50-
| Environment variable | Description | Utility | Default |
51-
|-------------------------------------------| --------------------------------------------------------------------------------- |---------------------------|-----------------------|
52-
| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `"service_undefined"` |
53-
| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | `None` |
54-
| **POWERTOOLS_TRACE_ENABLED** | Explicitly disables tracing | [Tracer](./core/tracer) | `true` |
55-
| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracer](./core/tracer) | `true` |
56-
| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracer](./core/tracer) | `true` |
57-
| **POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS** | Captures HTTP(s) requests as segments. | [Tracer](./core/tracer) | `true` |
58-
| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logger](./core/logger) | `false` |
59-
| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logger](./core/logger) | `0` |
60-
| **POWERTOOLS_LOG_DEDUPLICATION_DISABLED** | Disables log deduplication filter protection to use Pytest Live Log feature | [Logger](./core/logger) | `false` |
61-
| **LOG_LEVEL** | Sets logging level | [Logger](./core/logger) | `INFO` |
48+
**Explicit parameters passed in constructors or in middleware/decorators take precedence over environment variables.**
49+
50+
| Environment variable | Description | Utility | Default |
51+
|----------------------------------------------|----------------------------------------------------------------------------------------|---------------------------|-----------------------|
52+
| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `"service_undefined"` |
53+
| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | `None` |
54+
| **POWERTOOLS_TRACE_ENABLED** | Explicitly disables tracing | [Tracer](./core/tracer) | `true` |
55+
| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracer](./core/tracer) | `true` |
56+
| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracer](./core/tracer) | `true` |
57+
| **POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS** | Captures HTTP(s) requests as segments. | [Tracer](./core/tracer) | `true` |
58+
| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logger](./core/logger) | `false` |
59+
| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logger](./core/logger) | `0` |
60+
| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming events | [Logger](./core/logger) | `false` |
61+
| **LOG_LEVEL** | Sets logging level | [Logger](./core/logger) | `INFO` |
6262

6363
## Examples
6464

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
"lerna-preversion": "lerna exec -- npm run lint",
3131
"lerna-version": "lerna exec -- npm run format && git add -A src",
3232
"postversion": "git push && git push --tags",
33+
"docs-website-build-run": "npm run docs-buildDockerImage && npm run docs-runLocalDocker",
3334
"docs-buildDockerImage": "docker build -t powertool-typescript/docs ./docs/",
3435
"docs-runLocalDocker": "docker run --rm -it -p 8000:8000 -v ${PWD}:/docs powertool-typescript/docs",
36+
"docs-api-build-run": "npm run docs-generateApiDoc && npx live-server api",
3537
"docs-generateApiDoc": "typedoc .",
3638
"docs-runLocalApiDoc": "npx live-server api"
3739
},

packages/commons/tests/utils/InvocationLogs.ts

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ export class InvocationLogs {
5656
return filteredLogs.length > 0;
5757
}
5858

59+
/**
60+
* Return all the log of the function
61+
* @returns Array of function logs
62+
*/
63+
public getAllFunctionLogs(): string[] {
64+
return this.logs;
65+
}
66+
5967
/**
6068
* Return only logs from function, exclude START, END, and REPORT generated by Lambda service
6169
* @param levelToFilter level to filter

packages/logger/src/Logger.ts

+46-2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ class Logger extends Utility implements ClassThatLogs {
119119

120120
private envVarsService?: EnvironmentVariablesService;
121121

122+
private logEvent: boolean = false;
123+
122124
private logFormatter?: LogFormatterInterface;
123125

124126
private logLevel?: LogLevel;
@@ -222,6 +224,16 @@ class Logger extends Utility implements ClassThatLogs {
222224
this.processLogItem('ERROR', input, extraInput);
223225
}
224226

227+
/**
228+
* It returns a boolean value. True means that the Lambda invocation events
229+
* are printed in the logs.
230+
*
231+
* @returns {boolean}
232+
*/
233+
public getLogEvent(): boolean {
234+
return this.logEvent;
235+
}
236+
225237
/**
226238
* It returns a boolean value, if true all the logs will be printed.
227239
*
@@ -280,6 +292,9 @@ class Logger extends Utility implements ClassThatLogs {
280292
}
281293

282294
this.addContext(context);
295+
if (options) {
296+
this.logEventIfEnabled(event, options.logEvent);
297+
}
283298

284299
/* eslint-disable @typescript-eslint/no-non-null-assertion */
285300
const result = originalMethod!.apply(target, [ event, context, callback ]);
@@ -293,6 +308,20 @@ class Logger extends Utility implements ClassThatLogs {
293308
};
294309
}
295310

311+
/**
312+
* Logs a Lambda invocation event, if it *should*.
313+
*
314+
** @param {unknown} event
315+
* @param {boolean} [overwriteValue]
316+
* @returns {void}
317+
*/
318+
public logEventIfEnabled(event: unknown, overwriteValue?: boolean): void {
319+
if (!this.shouldLogEvent(overwriteValue)) {
320+
return;
321+
}
322+
this.info('Lambda invocation event', { event });
323+
}
324+
296325
/**
297326
* If the sample rate feature is enabled, the calculation that determines whether the logs
298327
* will actually be printed or not for this invocation is done when the Logger class is
@@ -353,6 +382,21 @@ class Logger extends Utility implements ClassThatLogs {
353382
this.getEnvVarsService().getSampleRateValue();
354383
}
355384

385+
/**
386+
* It checks whether the current Lambda invocation event should be printed in the logs or not.
387+
*
388+
* @private
389+
* @param {boolean} [overwriteValue]
390+
* @returns {boolean}
391+
*/
392+
public shouldLogEvent(overwriteValue?: boolean): boolean {
393+
if (typeof overwriteValue === 'boolean') {
394+
return overwriteValue;
395+
}
396+
397+
return this.getLogEvent();
398+
}
399+
356400
/**
357401
* It prints a log item with level WARN.
358402
*
@@ -486,10 +530,10 @@ class Logger extends Utility implements ClassThatLogs {
486530

487531
/**
488532
* It returns the current X-Ray Trace ID parsing the content of the `_X_AMZN_TRACE_ID` env variable.
489-
*
533+
*
490534
* The X-Ray Trace data available in the environment variable has this format:
491535
* `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`,
492-
*
536+
*
493537
* The actual Trace ID is: `1-5759e988-bd862e3fe1be46a994272793`.
494538
*
495539
* @private

packages/logger/src/config/ConfigService.ts

+19
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ abstract class ConfigService implements ConfigServiceInterface {
1919
* @protected
2020
*/
2121
protected currentEnvironmentVariable = 'ENVIRONMENT';
22+
protected logEventVariable = 'POWERTOOLS_LOGGER_LOG_EVENT';
2223
protected logLevelVariable = 'LOG_LEVEL';
2324
protected sampleRateValueVariable = 'POWERTOOLS_LOGGER_SAMPLE_RATE';
2425
protected serviceNameVariable = 'POWERTOOLS_SERVICE_NAME';
@@ -38,6 +39,13 @@ abstract class ConfigService implements ConfigServiceInterface {
3839
*/
3940
public abstract getCurrentEnvironment(): string;
4041

42+
/**
43+
* It returns the value of the POWERTOOLS_LOGGER_LOG_EVENT environment variable.
44+
*
45+
* @returns {boolean}
46+
*/
47+
public abstract getLogEvent(): boolean;
48+
4149
/**
4250
* It returns the value of the LOG_LEVEL environment variable.
4351
*
@@ -59,6 +67,17 @@ abstract class ConfigService implements ConfigServiceInterface {
5967
*/
6068
public abstract getServiceName(): string;
6169

70+
/**
71+
* It returns true if the string value represents a boolean true value.
72+
*
73+
* @param {string} value
74+
* @returns boolean
75+
* @protected
76+
*/
77+
protected isValueTrue(value: string): boolean {
78+
return value.toLowerCase() === 'true' || value === '1';
79+
}
80+
6281
}
6382

6483
export {

packages/logger/src/config/ConfigServiceInterface.ts

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ interface ConfigServiceInterface {
2222
*/
2323
getCurrentEnvironment(): string
2424

25+
/**
26+
* It returns the value of the POWERTOOLS_LOGGER_LOG_EVENT environment variable.
27+
*
28+
* @returns {boolean}
29+
*/
30+
getLogEvent(): boolean
31+
2532
/**
2633
* It returns the value of the LOG_LEVEL environment variable.
2734
*

packages/logger/src/config/EnvironmentVariablesService.ts

+9
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ class EnvironmentVariablesService extends ConfigService {
7979
return this.get(this.functionVersionVariable);
8080
}
8181

82+
/**
83+
* It returns the value of the POWERTOOLS_LOGGER_LOG_EVENT environment variable.
84+
*
85+
* @returns {boolean}
86+
*/
87+
public getLogEvent(): boolean {
88+
return this.isValueTrue(this.get(this.logEventVariable));
89+
}
90+
8291
/**
8392
* It returns the value of the LOG_LEVEL environment variable.
8493
*

packages/logger/src/middleware/middy.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ const injectLambdaContext = (target: Logger | Logger[], options?: HandlerOptions
3838
if (options && options.clearState === true) {
3939
persistentAttributes.push({ ...logger.getPersistentLogAttributes() });
4040
}
41+
if (options) {
42+
logger.logEventIfEnabled(request.event, options.logEvent);
43+
}
4144
});
4245
};
4346

@@ -48,7 +51,7 @@ const injectLambdaContext = (target: Logger | Logger[], options?: HandlerOptions
4851
});
4952
}
5053
};
51-
54+
5255
return {
5356
before: injectLambdaContextBefore,
5457
after: injectLambdaContextAfter

packages/logger/src/types/Logger.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type ClassThatLogs = {
99
};
1010

1111
type HandlerOptions = {
12+
logEvent?: boolean
1213
clearState?: boolean
1314
};
1415

packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Context, APIGatewayAuthorizerResult } from 'aws-lambda';
33
import middy from '@middy/core';
44

55
const PERSISTENT_KEY = process.env.PERSISTENT_KEY;
6+
const PERSISTENT_KEY_FIRST_INVOCATION_ONLY = process.env.PERSISTENT_KEY_FIRST_INVOCATION_ONLY;
67
const PERSISTENT_VALUE = process.env.PERSISTENT_VALUE;
78
const REMOVABLE_KEY = process.env.REMOVABLE_KEY;
89
const REMOVABLE_VALUE = process.env.REMOVABLE_VALUE;
@@ -33,7 +34,7 @@ const testFunction = async (event: LambdaEvent, context: Context): Promise<{requ
3334
const specialValue = event.invocation;
3435
if (specialValue === 0) {
3536
logger.appendKeys({
36-
specialKey: specialValue
37+
[PERSISTENT_KEY_FIRST_INVOCATION_ONLY]: specialValue
3738
});
3839
}
3940

@@ -72,4 +73,5 @@ const testFunction = async (event: LambdaEvent, context: Context): Promise<{requ
7273
};
7374
};
7475

75-
export const handler = middy(testFunction).use(injectLambdaContext(logger, { clearState: true }));
76+
export const handler = middy(testFunction)
77+
.use(injectLambdaContext(logger, { clearState: true, logEvent: true }));

0 commit comments

Comments
 (0)