1
- import pino , { DestinationStream , LevelMapping , LoggerOptions , Logger } from 'pino' ;
2
- import { GlobalContextStorageProvider , ContextStorageProvider , ContextMap } from './context' ;
3
-
4
- export interface ExtendedPinoOptions extends LoggerOptions {
5
- requestMixin ?: (
6
- event : LamdbaEvent ,
7
- context : LambdaContext ,
8
- ) => { [ key : string ] : string | undefined } ;
9
- storageProvider ?: ContextStorageProvider ;
10
- streamWriter ?: ( str : string | Uint8Array ) => boolean ;
11
- }
12
-
13
- interface LambdaContext {
14
- awsRequestId : string ;
15
-
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- [ key : string ] : any ;
18
- }
19
-
20
- interface LamdbaEvent {
21
- headers ?: {
22
- [ key : string ] : string | undefined ;
23
- } ;
24
-
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- [ key : string ] : any ;
27
- }
28
-
29
- interface PinoLambdaExtensionOptions {
30
- levels : LevelMapping ;
31
- options : ExtendedPinoOptions ;
32
- }
33
-
34
- const AMAZON_TRACE_ID = '_X_AMZN_TRACE_ID' ;
35
- const CORRELATION_HEADER = 'x-correlation-' ;
36
- const CORRELATION_ID = `${ CORRELATION_HEADER } id` ;
37
- const CORRELATION_TRACE_ID = `${ CORRELATION_HEADER } trace-id` ;
38
- const CORRELATION_DEBUG = `${ CORRELATION_HEADER } debug` ;
39
-
40
- const formatLevel = ( level : string | number , levels : LevelMapping ) : string => {
41
- if ( typeof level === 'string' ) {
42
- return level . toLocaleUpperCase ( ) ;
43
- } else if ( typeof level === 'number' ) {
44
- return levels . labels [ level ] ?. toLocaleUpperCase ( ) ;
45
- }
46
- return level ;
47
- } ;
48
-
49
- /**
50
- * Custom destination stream for Pino
51
- * @param options Pino options
52
- * @param storageProvider Global storage provider for request values
53
- */
54
- const pinolambda = ( { levels, options } : PinoLambdaExtensionOptions ) : DestinationStream => ( {
55
- write ( buffer : string ) {
56
- let output = buffer ;
57
- if ( ! options . prettyPrint ) {
58
- /**
59
- * Writes to stdout using the same method that AWS lambda uses
60
- * under the hood for console.log
61
- * This preserves the default log format of cloudwatch
62
- */
63
- const { level, msg } = JSON . parse ( buffer ) ;
64
- const storageProvider = options . storageProvider || GlobalContextStorageProvider ;
65
- const { awsRequestId } = storageProvider . getContext ( ) || { } ;
66
- const time = new Date ( ) . toISOString ( ) ;
67
- const levelTag = formatLevel ( level , levels ) ;
68
-
69
- output = `${ time } ${ awsRequestId ? `\t${ awsRequestId } ` : '' } \t${ levelTag } \t${ msg } \t${ buffer } ` ;
70
- output = output . replace ( / \n / , '\r' ) ;
71
- output += '\n' ;
72
- }
73
-
74
- if ( options . streamWriter ) {
75
- return options . streamWriter ( output ) ;
76
- }
77
- return process . stdout . write ( output ) ;
78
- } ,
79
- } ) ;
80
-
81
- export type PinoLambdaLogger = Logger & {
82
- withRequest : ( event : LamdbaEvent , context : LambdaContext ) => void ;
83
- } ;
1
+ import pino from 'pino' ;
2
+ import { GlobalContextStorageProvider } from './context' ;
3
+ import { ExtendedPinoOptions , PinoLambdaLogger } from './types' ;
4
+ import { withRequest } from './request' ;
5
+ import { createStream } from './stream' ;
84
6
85
7
/**
86
8
* Exports a default constructor with an extended instance of Pino
87
9
* that provides convinience methods for use with AWS Lambda
88
10
*/
89
11
export default ( extendedPinoOptions ?: ExtendedPinoOptions ) : PinoLambdaLogger => {
90
12
const options = extendedPinoOptions ?? { } ;
91
- const storageProvider = extendedPinoOptions ?. storageProvider || GlobalContextStorageProvider ;
13
+ const storageProvider = ( options . storageProvider =
14
+ extendedPinoOptions ?. storageProvider || GlobalContextStorageProvider ) ;
92
15
93
16
// attach request values to logs
94
17
const pinoOptions = {
@@ -104,65 +27,14 @@ export default (extendedPinoOptions?: ExtendedPinoOptions): PinoLambdaLogger =>
104
27
} ;
105
28
106
29
// construct a pino logger and set its destination
107
- const logger = ( pino (
108
- pinoOptions ,
109
- pinolambda ( { options : pinoOptions , levels : pino . levels } ) ,
110
- ) as unknown ) as PinoLambdaLogger ;
111
-
112
- // keep a reference to the original logger level
113
- const configuredLevel = logger . level ;
30
+ const logger = ( pino ( pinoOptions , createStream ( pinoOptions ) ) as unknown ) as PinoLambdaLogger ;
114
31
115
32
// extend the base logger
116
- logger . withRequest = ( event : LamdbaEvent , context : LambdaContext ) : void => {
117
- const ctx : ContextMap = {
118
- awsRequestId : context . awsRequestId ,
119
- } ;
120
-
121
- // capture api gateway request ID
122
- const apiRequestId = event . requestContext ?. requestId ;
123
- if ( apiRequestId ) {
124
- ctx . apiRequestId = apiRequestId ;
125
- }
126
-
127
- // capture any correlation headers sent from upstream callers
128
- if ( event . headers ) {
129
- Object . keys ( event . headers ) . forEach ( ( header ) => {
130
- if ( header . toLowerCase ( ) . startsWith ( CORRELATION_HEADER ) ) {
131
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
132
- ctx [ header ] = event . headers ! [ header ] as string ;
133
- }
134
- } ) ;
135
- }
136
-
137
- // capture the xray trace id if its enabled
138
- if ( process . env [ AMAZON_TRACE_ID ] ) {
139
- ctx [ CORRELATION_TRACE_ID ] = process . env [ AMAZON_TRACE_ID ] as string ;
140
- }
33
+ logger . withRequest = withRequest ( logger , pinoOptions ) ;
141
34
142
- // set the correlation id if not already set by upstream callers
143
- if ( ! ctx [ CORRELATION_ID ] ) {
144
- ctx [ CORRELATION_ID ] = context . awsRequestId ;
145
- }
146
-
147
- // if an upstream service requests DEBUG mode,
148
- // dynamically modify the logging level
149
- if ( ctx [ CORRELATION_DEBUG ] === 'true' ) {
150
- logger . level = 'debug' ;
151
- } else {
152
- logger . level = configuredLevel ;
153
- }
154
-
155
- // handle custom request level mixins
156
- if ( pinoOptions . requestMixin ) {
157
- const result = pinoOptions . requestMixin ( event , context ) ;
158
- for ( const key in result ) {
159
- // Cast this to string for typescript
160
- // when the JSON serializer runs, by default it omits undefined properties
161
- ctx [ key ] = result [ key ] as string ;
162
- }
163
- }
164
-
165
- storageProvider . setContext ( ctx ) ;
166
- } ;
167
35
return logger ;
168
36
} ;
37
+
38
+ // reexport all public types
39
+ export * from './formatters' ;
40
+ export * from './types' ;
0 commit comments