serverless-event-logger 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,262 @@
1
+ # serverless-event-logger
2
+
3
+ 🚀 Lightweight, zero-dependency structured JSON logger for AWS Lambda. Optimized for observability with automatic context extraction from serverless events.
4
+
5
+ [![npm version](https://badge.fury.io/js/serverless-event-logger.svg)](https://www.npmjs.com/package/serverless-event-logger)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - **Zero dependencies** - Minimal bundle size (< 5KB minified)
11
+ - **Structured JSON output** - Perfect for CloudWatch Insights queries
12
+ - **Automatic context extraction** - Captures requestId, userId, segment from events
13
+ - **Log level filtering** - debug, info, warn, error
14
+ - **Sensitive data redaction** - Automatically redact passwords, tokens, etc.
15
+ - **Timer utility** - Measure operation duration
16
+ - **Child loggers** - Add module-specific context
17
+ - **TypeScript first** - Full type definitions included
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install serverless-event-logger
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { createLogger } from 'serverless-event-logger';
29
+
30
+ // Create a logger instance
31
+ const logger = createLogger({
32
+ service: 'my-lambda',
33
+ });
34
+
35
+ // Basic logging
36
+ logger.info('User created', { userId: '123' });
37
+ // Output: {"timestamp":"2024-01-15T10:30:00.000Z","level":"info","message":"User created","service":"my-lambda","userId":"123"}
38
+
39
+ logger.error('Database connection failed', { error: err.message });
40
+ ```
41
+
42
+ ## Usage with Lambda Events
43
+
44
+ ```typescript
45
+ import { createLogger } from 'serverless-event-logger';
46
+ import { NormalizedEvent } from 'serverless-event-orchestrator';
47
+
48
+ const logger = createLogger({
49
+ service: 'ml-properties-lambda',
50
+ });
51
+
52
+ async function handler(event: NormalizedEvent) {
53
+ // Create logger with request context
54
+ const log = logger.withContext(event);
55
+
56
+ log.info('Handler started');
57
+ // Output includes: requestId, userId, segment, path, method
58
+
59
+ try {
60
+ const result = await processRequest(event);
61
+ log.info('Request processed', { resultId: result.id });
62
+ return result;
63
+ } catch (error) {
64
+ log.error('Request failed', { error: error.message, stack: error.stack });
65
+ throw error;
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## API Reference
71
+
72
+ ### `createLogger(config)`
73
+
74
+ Creates a new logger instance.
75
+
76
+ ```typescript
77
+ interface LoggerConfig {
78
+ service: string; // REQUIRED: Name of your lambda/service
79
+ defaultLevel?: LogLevel; // Default: 'info'
80
+ silent?: boolean; // Default: false (set true for tests)
81
+ redactPaths?: string[]; // Fields to redact (e.g., ['password', 'token'])
82
+ timestampFormat?: 'iso' | 'epoch'; // Default: 'iso'
83
+ pretty?: boolean; // Default: false (formatted output for local dev)
84
+ }
85
+
86
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
87
+ ```
88
+
89
+ ### Logging Methods
90
+
91
+ ```typescript
92
+ logger.debug(message: string, data?: object): void
93
+ logger.info(message: string, data?: object): void
94
+ logger.warn(message: string, data?: object): void
95
+ logger.error(message: string, data?: object): void
96
+ ```
97
+
98
+ ### `logger.withContext(event)`
99
+
100
+ Creates a new logger with context extracted from a `NormalizedEvent`:
101
+
102
+ ```typescript
103
+ const log = logger.withContext(event);
104
+ // Extracts: requestId, userId, segment, path, method, userAgent
105
+ ```
106
+
107
+ ### `logger.child(fields)`
108
+
109
+ Creates a child logger with additional fixed fields:
110
+
111
+ ```typescript
112
+ const dbLogger = logger.child({ module: 'dynamodb' });
113
+ dbLogger.info('Query executed', { table: 'Users' });
114
+ // Output includes module: 'dynamodb'
115
+ ```
116
+
117
+ ### `logger.startTimer(label)`
118
+
119
+ Measures operation duration:
120
+
121
+ ```typescript
122
+ const timer = log.startTimer('database-query');
123
+ await db.query(...);
124
+ timer.done({ recordsFound: 150 });
125
+ // Output: {"message":"database-query completed","duration":234,"recordsFound":150,...}
126
+ ```
127
+
128
+ ## Configuration
129
+
130
+ ### Environment Variables
131
+
132
+ | Variable | Description | Default |
133
+ |----------|-------------|---------|
134
+ | `LOG_LEVEL` | Minimum log level | `info` |
135
+ | `LOG_SILENT` | Disable all logs | `false` |
136
+ | `LOG_PRETTY` | Formatted output | `false` |
137
+
138
+ ### Sensitive Data Redaction
139
+
140
+ ```typescript
141
+ const logger = createLogger({
142
+ service: 'auth-lambda',
143
+ redactPaths: ['password', 'token', 'authorization', 'apiKey'],
144
+ });
145
+
146
+ logger.info('Login attempt', {
147
+ username: 'john',
148
+ password: 'secret123'
149
+ });
150
+ // Output: {..., "username": "john", "password": "[REDACTED]"}
151
+ ```
152
+
153
+ ## Log Output Structure
154
+
155
+ ### Base Fields (always present)
156
+
157
+ ```json
158
+ {
159
+ "timestamp": "2024-01-15T10:30:00.000Z",
160
+ "level": "info",
161
+ "message": "Your log message",
162
+ "service": "your-service-name"
163
+ }
164
+ ```
165
+
166
+ ### Context Fields (with `withContext`)
167
+
168
+ ```json
169
+ {
170
+ "requestId": "abc-123-def-456",
171
+ "userId": "user_abc123",
172
+ "segment": "private",
173
+ "path": "/api/users",
174
+ "method": "POST",
175
+ "userAgent": "Mozilla/5.0..."
176
+ }
177
+ ```
178
+
179
+ ## CloudWatch Insights Queries
180
+
181
+ ### Find errors by endpoint
182
+
183
+ ```sql
184
+ fields @timestamp, message, path, method, error, userId
185
+ | filter level = "error"
186
+ | filter service = "my-lambda"
187
+ | sort @timestamp desc
188
+ | limit 100
189
+ ```
190
+
191
+ ### Latency analysis
192
+
193
+ ```sql
194
+ fields @timestamp, path, method, duration
195
+ | filter message like /completed/
196
+ | stats avg(duration) as avg_ms, max(duration) as max_ms by path, method
197
+ | sort avg_ms desc
198
+ ```
199
+
200
+ ### User activity trace
201
+
202
+ ```sql
203
+ fields @timestamp, level, message, path
204
+ | filter userId = "user_abc123"
205
+ | sort @timestamp desc
206
+ | limit 50
207
+ ```
208
+
209
+ ## TypeScript Support
210
+
211
+ Full TypeScript definitions are included:
212
+
213
+ ```typescript
214
+ import {
215
+ createLogger,
216
+ Logger,
217
+ LoggerConfig,
218
+ LogLevel,
219
+ Timer,
220
+ NormalizedEventLike
221
+ } from 'serverless-event-logger';
222
+ ```
223
+
224
+ ## Best Practices
225
+
226
+ 1. **Create one logger per service** - Initialize at module level
227
+ 2. **Use `withContext` in handlers** - Captures request-specific info
228
+ 3. **Use `child` for modules** - Adds component context
229
+ 4. **Redact sensitive data** - Configure `redactPaths`
230
+ 5. **Use timers for slow operations** - Helps identify bottlenecks
231
+
232
+ ```typescript
233
+ // src/logging.ts
234
+ export const logger = createLogger({
235
+ service: process.env.LAMBDA_NAME || 'unknown-lambda',
236
+ redactPaths: ['password', 'token', 'authorization'],
237
+ });
238
+
239
+ // src/handlers/user.ts
240
+ import { logger } from '../logging';
241
+
242
+ export async function createUser(event: NormalizedEvent) {
243
+ const log = logger.withContext(event);
244
+ log.info('Creating user', { email: event.payload.body.email });
245
+ // ...
246
+ }
247
+
248
+ // src/repositories/user-repository.ts
249
+ import { logger } from '../logging';
250
+
251
+ const log = logger.child({ module: 'UserRepository' });
252
+
253
+ export async function save(user: User) {
254
+ const timer = log.startTimer('dynamodb-put');
255
+ await db.put(user);
256
+ timer.done({ userId: user.id });
257
+ }
258
+ ```
259
+
260
+ ## License
261
+
262
+ MIT © MLHolding
@@ -0,0 +1,23 @@
1
+ import type { LogLevel, ResolvedLoggerConfig } from './types';
2
+ /**
3
+ * Log level priority mapping.
4
+ * Lower number = more verbose (will log more)
5
+ */
6
+ export declare const LOG_LEVELS: Record<LogLevel, number>;
7
+ /**
8
+ * Default configuration values
9
+ */
10
+ export declare const DEFAULT_CONFIG: Omit<ResolvedLoggerConfig, 'service'>;
11
+ /**
12
+ * Environment variable names
13
+ */
14
+ export declare const ENV_VARS: {
15
+ readonly LOG_LEVEL: "LOG_LEVEL";
16
+ readonly LOG_SILENT: "LOG_SILENT";
17
+ readonly LOG_PRETTY: "LOG_PRETTY";
18
+ };
19
+ /**
20
+ * Redaction placeholder
21
+ */
22
+ export declare const REDACTED_VALUE = "[REDACTED]";
23
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAMtC,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAMvD,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc,eAAe,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Log level priority mapping.
3
+ * Lower number = more verbose (will log more)
4
+ */
5
+ export const LOG_LEVELS = {
6
+ debug: 0,
7
+ info: 1,
8
+ warn: 2,
9
+ error: 3,
10
+ silent: 4,
11
+ };
12
+ /**
13
+ * Default configuration values
14
+ */
15
+ export const DEFAULT_CONFIG = {
16
+ defaultLevel: 'info',
17
+ silent: false,
18
+ redactPaths: [],
19
+ timestampFormat: 'iso',
20
+ pretty: false,
21
+ };
22
+ /**
23
+ * Environment variable names
24
+ */
25
+ export const ENV_VARS = {
26
+ LOG_LEVEL: 'LOG_LEVEL',
27
+ LOG_SILENT: 'LOG_SILENT',
28
+ LOG_PRETTY: 'LOG_PRETTY',
29
+ };
30
+ /**
31
+ * Redaction placeholder
32
+ */
33
+ export const REDACTED_VALUE = '[REDACTED]';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAEA;;;GAGG;AACU,QAAA,UAAU,GAA6B;IAClD,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;CACD,CAAC;AAEX;;GAEG;AACU,QAAA,cAAc,GAA0C;IACnE,YAAY,EAAE,MAAM;IACpB,MAAM,EAAE,KAAK;IACb,WAAW,EAAE,EAAE;IACf,eAAe,EAAE,KAAK;IACtB,MAAM,EAAE,KAAK;CACL,CAAC;AAEX;;GAEG;AACU,QAAA,QAAQ,GAAG;IACtB,SAAS,EAAE,WAAW;IACtB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;CAChB,CAAC;AAEX;;GAEG;AACU,QAAA,cAAc,GAAG,YAAY,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * serverless-event-logger
3
+ *
4
+ * Lightweight, zero-dependency structured JSON logger for AWS Lambda.
5
+ * Optimized for observability with automatic context extraction from serverless events.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { Logger, createLogger } from './logger';
10
+ export type { LogLevel, LoggerConfig, Timer, ILogger, LogEntry, ContextFields, AdditionalFields, LogOutput, NormalizedEventLike, TimestampFormat, } from './types';
11
+ export { LOG_LEVELS, REDACTED_VALUE } from './constants';
12
+ export { extractContext, generateRequestId } from './utils/extract-context';
13
+ export { serializeError } from './utils/format';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEhD,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,OAAO,EACP,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,mBAAmB,EACnB,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * serverless-event-logger
3
+ *
4
+ * Lightweight, zero-dependency structured JSON logger for AWS Lambda.
5
+ * Optimized for observability with automatic context extraction from serverless events.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { Logger, createLogger } from './logger';
10
+ export { LOG_LEVELS, REDACTED_VALUE } from './constants';
11
+ export { extractContext, generateRequestId } from './utils/extract-context';
12
+ export { serializeError } from './utils/format';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,mCAAgD;AAAvC,gGAAA,MAAM,OAAA;AAAE,sGAAA,YAAY,OAAA;AAe7B,yCAAyD;AAAhD,uGAAA,UAAU,OAAA;AAAE,2GAAA,cAAc,OAAA;AAEnC,2DAA4E;AAAnE,iHAAA,cAAc,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAC1C,yCAAgD;AAAvC,wGAAA,cAAc,OAAA"}
@@ -0,0 +1,67 @@
1
+ import type { LoggerConfig, Timer, ILogger, NormalizedEventLike } from './types';
2
+ /**
3
+ * Logger class for structured JSON logging.
4
+ * Optimized for AWS Lambda and CloudWatch Insights.
5
+ */
6
+ export declare class Logger implements ILogger {
7
+ private readonly config;
8
+ private readonly contextData;
9
+ constructor(config: LoggerConfig, context?: Record<string, unknown>);
10
+ /**
11
+ * Creates a new Logger instance with context extracted from a NormalizedEvent.
12
+ * The new logger includes requestId, userId, segment, path, method from the event.
13
+ */
14
+ withContext(event: NormalizedEventLike): Logger;
15
+ /**
16
+ * Creates a child logger with additional fixed fields.
17
+ * Useful for adding module-specific context.
18
+ */
19
+ child(fields: Record<string, unknown>): Logger;
20
+ /**
21
+ * Log a debug message. Only outputs if LOG_LEVEL is 'debug'.
22
+ */
23
+ debug(message: string, data?: Record<string, unknown>): void;
24
+ /**
25
+ * Log an info message.
26
+ */
27
+ info(message: string, data?: Record<string, unknown>): void;
28
+ /**
29
+ * Log a warning message.
30
+ */
31
+ warn(message: string, data?: Record<string, unknown>): void;
32
+ /**
33
+ * Log an error message.
34
+ */
35
+ error(message: string, data?: Record<string, unknown>): void;
36
+ /**
37
+ * Starts a timer for measuring operation duration.
38
+ * Call timer.done() when the operation completes.
39
+ *
40
+ * @param label - Description of the operation being timed
41
+ * @returns Timer object with done() method
42
+ */
43
+ startTimer(label: string): Timer;
44
+ /**
45
+ * Internal method to output a log entry
46
+ */
47
+ private log;
48
+ /**
49
+ * Redacts sensitive fields from data
50
+ */
51
+ private redact;
52
+ }
53
+ /**
54
+ * Factory function to create a new Logger instance.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const logger = createLogger({
59
+ * service: 'my-lambda',
60
+ * defaultLevel: 'info',
61
+ * });
62
+ *
63
+ * logger.info('Hello world', { userId: '123' });
64
+ * ```
65
+ */
66
+ export declare function createLogger(config: LoggerConfig): Logger;
67
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAIZ,KAAK,EACL,OAAO,EACP,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAsBjB;;;GAGG;AACH,qBAAa,MAAO,YAAW,OAAO;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;gBAE1C,MAAM,EAAE,YAAY,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAKvE;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM;IAQ/C;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IAO9C;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D;;;;;;OAMG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK;IAUhC;;OAEG;IACH,OAAO,CAAC,GAAG;IA4CX;;OAEG;IACH,OAAO,CAAC,MAAM;CAMf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEzD"}
package/dist/logger.js ADDED
@@ -0,0 +1,148 @@
1
+ import { LOG_LEVELS, DEFAULT_CONFIG, ENV_VARS } from './constants';
2
+ import { redactFields, getTimestamp, formatLogEntry, extractContext } from './utils';
3
+ /**
4
+ * Resolves configuration by merging provided config with defaults and environment variables
5
+ */
6
+ function resolveConfig(config) {
7
+ const envLevel = process.env[ENV_VARS.LOG_LEVEL];
8
+ const envSilent = process.env[ENV_VARS.LOG_SILENT] === 'true';
9
+ const envPretty = process.env[ENV_VARS.LOG_PRETTY] === 'true';
10
+ return {
11
+ service: config.service,
12
+ defaultLevel: config.defaultLevel ?? envLevel ?? DEFAULT_CONFIG.defaultLevel,
13
+ silent: config.silent ?? envSilent ?? DEFAULT_CONFIG.silent,
14
+ redactPaths: config.redactPaths ?? DEFAULT_CONFIG.redactPaths,
15
+ timestampFormat: config.timestampFormat ?? DEFAULT_CONFIG.timestampFormat,
16
+ pretty: config.pretty ?? envPretty ?? DEFAULT_CONFIG.pretty,
17
+ };
18
+ }
19
+ /**
20
+ * Logger class for structured JSON logging.
21
+ * Optimized for AWS Lambda and CloudWatch Insights.
22
+ */
23
+ export class Logger {
24
+ constructor(config, context = {}) {
25
+ this.config = resolveConfig(config);
26
+ this.contextData = context;
27
+ }
28
+ /**
29
+ * Creates a new Logger instance with context extracted from a NormalizedEvent.
30
+ * The new logger includes requestId, userId, segment, path, method from the event.
31
+ */
32
+ withContext(event) {
33
+ const eventContext = extractContext(event);
34
+ return new Logger(this.config, { ...this.contextData, ...eventContext });
35
+ }
36
+ /**
37
+ * Creates a child logger with additional fixed fields.
38
+ * Useful for adding module-specific context.
39
+ */
40
+ child(fields) {
41
+ return new Logger(this.config, { ...this.contextData, ...fields });
42
+ }
43
+ /**
44
+ * Log a debug message. Only outputs if LOG_LEVEL is 'debug'.
45
+ */
46
+ debug(message, data) {
47
+ this.log('debug', message, data);
48
+ }
49
+ /**
50
+ * Log an info message.
51
+ */
52
+ info(message, data) {
53
+ this.log('info', message, data);
54
+ }
55
+ /**
56
+ * Log a warning message.
57
+ */
58
+ warn(message, data) {
59
+ this.log('warn', message, data);
60
+ }
61
+ /**
62
+ * Log an error message.
63
+ */
64
+ error(message, data) {
65
+ this.log('error', message, data);
66
+ }
67
+ /**
68
+ * Starts a timer for measuring operation duration.
69
+ * Call timer.done() when the operation completes.
70
+ *
71
+ * @param label - Description of the operation being timed
72
+ * @returns Timer object with done() method
73
+ */
74
+ startTimer(label) {
75
+ const start = Date.now();
76
+ return {
77
+ done: (data) => {
78
+ const duration = Date.now() - start;
79
+ this.info(`${label} completed`, { duration, ...data });
80
+ },
81
+ };
82
+ }
83
+ /**
84
+ * Internal method to output a log entry
85
+ */
86
+ log(level, message, data) {
87
+ // Check if logging is disabled
88
+ if (this.config.silent) {
89
+ return;
90
+ }
91
+ // Check log level threshold
92
+ const currentLevelPriority = LOG_LEVELS[level];
93
+ const configuredLevelPriority = LOG_LEVELS[this.config.defaultLevel];
94
+ if (currentLevelPriority === undefined || configuredLevelPriority === undefined) {
95
+ return;
96
+ }
97
+ if (currentLevelPriority < configuredLevelPriority) {
98
+ return;
99
+ }
100
+ // Build log entry
101
+ const entry = {
102
+ timestamp: getTimestamp(this.config.timestampFormat),
103
+ level,
104
+ message,
105
+ service: this.config.service,
106
+ ...this.contextData,
107
+ ...this.redact(data),
108
+ };
109
+ // Format output
110
+ const output = formatLogEntry(entry, this.config.pretty);
111
+ // Output to appropriate console method for CloudWatch categorization
112
+ switch (level) {
113
+ case 'error':
114
+ console.error(output);
115
+ break;
116
+ case 'warn':
117
+ console.warn(output);
118
+ break;
119
+ default:
120
+ console.log(output);
121
+ }
122
+ }
123
+ /**
124
+ * Redacts sensitive fields from data
125
+ */
126
+ redact(data) {
127
+ if (!data || this.config.redactPaths.length === 0) {
128
+ return data ?? {};
129
+ }
130
+ return redactFields(data, this.config.redactPaths);
131
+ }
132
+ }
133
+ /**
134
+ * Factory function to create a new Logger instance.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const logger = createLogger({
139
+ * service: 'my-lambda',
140
+ * defaultLevel: 'info',
141
+ * });
142
+ *
143
+ * logger.info('Hello world', { userId: '123' });
144
+ * ```
145
+ */
146
+ export function createLogger(config) {
147
+ return new Logger(config);
148
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;AAsLA,oCAEC;AA/KD,2CAAmE;AACnE,mCAAqF;AAErF;;GAEG;AACH,SAAS,aAAa,CAAC,MAAoB;IACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAQ,CAAC,SAAS,CAAyB,CAAC;IACzE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAQ,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAQ,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC;IAE9D,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,QAAQ,IAAI,0BAAc,CAAC,YAAY;QAC5E,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,SAAS,IAAI,0BAAc,CAAC,MAAM;QAC3D,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,0BAAc,CAAC,WAAW;QAC7D,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,0BAAc,CAAC,eAAe;QACzE,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,SAAS,IAAI,0BAAc,CAAC,MAAM;KAC5D,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAa,MAAM;IAIjB,YAAY,MAAoB,EAAE,UAAmC,EAAE;QACrE,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,KAA0B;QACpC,MAAM,YAAY,GAAG,IAAA,sBAAc,EAAC,KAAK,CAAC,CAAC;QAC3C,OAAO,IAAI,MAAM,CACf,IAAI,CAAC,MAAM,EACX,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,YAAY,EAAE,CACzC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAA+B;QACnC,OAAO,IAAI,MAAM,CACf,IAAI,CAAC,MAAM,EACX,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,MAAM,EAAE,CACnC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAe,EAAE,IAA8B;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,KAAa;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,OAAO;YACL,IAAI,EAAE,CAAC,IAA8B,EAAE,EAAE;gBACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,YAAY,EAAE,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;QAC1E,+BAA+B;QAC/B,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,4BAA4B;QAC5B,MAAM,oBAAoB,GAAG,sBAAU,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,uBAAuB,GAAG,sBAAU,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAErE,IAAI,oBAAoB,KAAK,SAAS,IAAI,uBAAuB,KAAK,SAAS,EAAE,CAAC;YAChF,OAAO;QACT,CAAC;QAED,IAAI,oBAAoB,GAAG,uBAAuB,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QAED,kBAAkB;QAClB,MAAM,KAAK,GAAc;YACvB,SAAS,EAAE,IAAA,oBAAY,EAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;YACpD,KAAK;YACL,OAAO;YACP,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;YAC5B,GAAG,IAAI,CAAC,WAAW;YACnB,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;SACrB,CAAC;QAEF,gBAAgB;QAChB,MAAM,MAAM,GAAG,IAAA,sBAAc,EAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzD,qEAAqE;QACrE,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACtB,MAAM;YACR,KAAK,MAAM;gBACT,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,MAAM;YACR;gBACE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,IAA8B;QAC3C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClD,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,CAAC;QACD,OAAO,IAAA,oBAAY,EAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACrD,CAAC;CACF;AArID,wBAqIC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,YAAY,CAAC,MAAoB;IAC/C,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Log levels supported by the logger
3
+ */
4
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
5
+ /**
6
+ * Timestamp format options
7
+ */
8
+ export type TimestampFormat = 'iso' | 'epoch';
9
+ /**
10
+ * Configuration options for creating a logger instance
11
+ */
12
+ export interface LoggerConfig {
13
+ /** REQUIRED: Name of the lambda/service */
14
+ service: string;
15
+ /** Minimum log level to output. Default: 'info' */
16
+ defaultLevel?: LogLevel;
17
+ /** If true, disables all log output (useful for tests). Default: false */
18
+ silent?: boolean;
19
+ /** Array of field names to redact from logs (e.g., ['password', 'token']) */
20
+ redactPaths?: string[];
21
+ /** Timestamp format. Default: 'iso' */
22
+ timestampFormat?: TimestampFormat;
23
+ /** If true, outputs formatted JSON (useful for local development). Default: false */
24
+ pretty?: boolean;
25
+ }
26
+ /**
27
+ * Internal resolved configuration (all fields required)
28
+ */
29
+ export interface ResolvedLoggerConfig {
30
+ service: string;
31
+ defaultLevel: LogLevel;
32
+ silent: boolean;
33
+ redactPaths: string[];
34
+ timestampFormat: TimestampFormat;
35
+ pretty: boolean;
36
+ }
37
+ /**
38
+ * Base fields always present in log output
39
+ */
40
+ export interface LogEntry {
41
+ timestamp: string | number;
42
+ level: LogLevel;
43
+ message: string;
44
+ service: string;
45
+ }
46
+ /**
47
+ * Context fields extracted from NormalizedEvent
48
+ */
49
+ export interface ContextFields {
50
+ requestId?: string;
51
+ userId?: string;
52
+ segment?: string;
53
+ path?: string;
54
+ method?: string;
55
+ userAgent?: string;
56
+ }
57
+ /**
58
+ * Additional fields that can be added to log entries
59
+ */
60
+ export interface AdditionalFields {
61
+ [key: string]: unknown;
62
+ error?: string;
63
+ stack?: string;
64
+ duration?: number;
65
+ module?: string;
66
+ }
67
+ /**
68
+ * Complete log output structure
69
+ */
70
+ export type LogOutput = LogEntry & ContextFields & AdditionalFields;
71
+ /**
72
+ * Timer interface returned by startTimer()
73
+ */
74
+ export interface Timer {
75
+ /** Call when the operation is complete */
76
+ done: (data?: Record<string, unknown>) => void;
77
+ }
78
+ /**
79
+ * Identity information from NormalizedEvent
80
+ */
81
+ export interface EventIdentity {
82
+ userId?: string;
83
+ [key: string]: unknown;
84
+ }
85
+ /**
86
+ * Context from NormalizedEvent
87
+ */
88
+ export interface EventContext {
89
+ requestId?: string;
90
+ identity?: EventIdentity;
91
+ segment?: string;
92
+ [key: string]: unknown;
93
+ }
94
+ /**
95
+ * Payload from NormalizedEvent
96
+ */
97
+ export interface EventPayload {
98
+ httpMethod?: string;
99
+ path?: string;
100
+ headers?: Record<string, string | undefined>;
101
+ [key: string]: unknown;
102
+ }
103
+ /**
104
+ * Minimal NormalizedEvent interface for context extraction.
105
+ * Compatible with serverless-event-orchestrator's NormalizedEvent.
106
+ */
107
+ export interface NormalizedEventLike {
108
+ context?: EventContext;
109
+ payload?: EventPayload;
110
+ [key: string]: unknown;
111
+ }
112
+ /**
113
+ * Logger interface defining the public API
114
+ */
115
+ export interface ILogger {
116
+ /** Log a debug message */
117
+ debug(message: string, data?: Record<string, unknown>): void;
118
+ /** Log an info message */
119
+ info(message: string, data?: Record<string, unknown>): void;
120
+ /** Log a warning message */
121
+ warn(message: string, data?: Record<string, unknown>): void;
122
+ /** Log an error message */
123
+ error(message: string, data?: Record<string, unknown>): void;
124
+ /** Create a new logger with context extracted from a NormalizedEvent */
125
+ withContext(event: NormalizedEventLike): ILogger;
126
+ /** Create a child logger with additional fixed fields */
127
+ child(fields: Record<string, unknown>): ILogger;
128
+ /** Start a timer for measuring operation duration */
129
+ startTimer(label: string): Timer;
130
+ }
131
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ import type { ContextFields, NormalizedEventLike } from '../types';
2
+ /**
3
+ * Generates a unique request ID
4
+ */
5
+ export declare function generateRequestId(): string;
6
+ /**
7
+ * Extracts logging context from a NormalizedEvent.
8
+ * Compatible with serverless-event-orchestrator's NormalizedEvent structure.
9
+ *
10
+ * @param event - The NormalizedEvent or similar event object
11
+ * @returns Context fields for logging
12
+ */
13
+ export declare function extractContext(event: NormalizedEventLike): ContextFields;
14
+ //# sourceMappingURL=extract-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-context.d.ts","sourceRoot":"","sources":["../../src/utils/extract-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEnE;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,aAAa,CAsCxE"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Generates a unique request ID
3
+ */
4
+ export function generateRequestId() {
5
+ const timestamp = Date.now().toString(36);
6
+ const random = Math.random().toString(36).substring(2, 11);
7
+ return `req_${timestamp}_${random}`;
8
+ }
9
+ /**
10
+ * Extracts logging context from a NormalizedEvent.
11
+ * Compatible with serverless-event-orchestrator's NormalizedEvent structure.
12
+ *
13
+ * @param event - The NormalizedEvent or similar event object
14
+ * @returns Context fields for logging
15
+ */
16
+ export function extractContext(event) {
17
+ const context = {};
18
+ // Extract requestId
19
+ context.requestId = event.context?.requestId || generateRequestId();
20
+ // Extract userId from identity
21
+ if (event.context?.identity?.userId) {
22
+ context.userId = event.context.identity.userId;
23
+ }
24
+ // Extract segment
25
+ if (event.context?.segment) {
26
+ context.segment = event.context.segment;
27
+ }
28
+ // Extract HTTP method
29
+ if (event.payload?.httpMethod) {
30
+ context.method = event.payload.httpMethod;
31
+ }
32
+ // Extract path
33
+ if (event.payload?.path) {
34
+ context.path = event.payload.path;
35
+ }
36
+ // Extract User-Agent (optional)
37
+ const userAgent = event.payload?.headers?.['user-agent'] ||
38
+ event.payload?.headers?.['User-Agent'];
39
+ if (userAgent) {
40
+ context.userAgent = userAgent;
41
+ }
42
+ // Remove undefined values
43
+ return Object.fromEntries(Object.entries(context).filter(([, value]) => value !== undefined));
44
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-context.js","sourceRoot":"","sources":["../../src/utils/extract-context.ts"],"names":[],"mappings":";;AAKA,8CAIC;AASD,wCAsCC;AAtDD;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3D,OAAO,OAAO,SAAS,IAAI,MAAM,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,KAA0B;IACvD,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,oBAAoB;IACpB,OAAO,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,EAAE,SAAS,IAAI,iBAAiB,EAAE,CAAC;IAEpE,+BAA+B;IAC/B,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACpC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACjD,CAAC;IAED,kBAAkB;IAClB,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;IAC1C,CAAC;IAED,sBAAsB;IACtB,IAAI,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;IAC5C,CAAC;IAED,eAAe;IACf,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;IACpC,CAAC;IAED,gCAAgC;IAChC,MAAM,SAAS,GACb,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC;QACtC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAChC,CAAC;IAED,0BAA0B;IAC1B,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAClD,CAAC;AACrB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { LogOutput, TimestampFormat } from '../types';
2
+ /**
3
+ * Get current timestamp in the specified format
4
+ */
5
+ export declare function getTimestamp(format: TimestampFormat): string | number;
6
+ /**
7
+ * Format a log entry as JSON string
8
+ *
9
+ * @param entry - The log entry to format
10
+ * @param pretty - If true, formats with indentation for readability
11
+ * @returns JSON string representation of the log entry
12
+ */
13
+ export declare function formatLogEntry(entry: LogOutput, pretty: boolean): string;
14
+ /**
15
+ * Safely serialize error objects for logging
16
+ */
17
+ export declare function serializeError(error: unknown): Record<string, unknown>;
18
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3D;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,MAAM,CAGrE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAKxE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBtE"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Get current timestamp in the specified format
3
+ */
4
+ export function getTimestamp(format) {
5
+ const now = new Date();
6
+ return format === 'epoch' ? now.getTime() : now.toISOString();
7
+ }
8
+ /**
9
+ * Format a log entry as JSON string
10
+ *
11
+ * @param entry - The log entry to format
12
+ * @param pretty - If true, formats with indentation for readability
13
+ * @returns JSON string representation of the log entry
14
+ */
15
+ export function formatLogEntry(entry, pretty) {
16
+ if (pretty) {
17
+ return JSON.stringify(entry, null, 2);
18
+ }
19
+ return JSON.stringify(entry);
20
+ }
21
+ /**
22
+ * Safely serialize error objects for logging
23
+ */
24
+ export function serializeError(error) {
25
+ if (error instanceof Error) {
26
+ return {
27
+ error: error.message,
28
+ stack: error.stack,
29
+ name: error.name,
30
+ };
31
+ }
32
+ if (typeof error === 'string') {
33
+ return { error };
34
+ }
35
+ if (typeof error === 'object' && error !== null) {
36
+ return error;
37
+ }
38
+ return { error: String(error) };
39
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":";;AAKA,oCAGC;AASD,wCAKC;AAKD,wCAkBC;AA3CD;;GAEG;AACH,SAAgB,YAAY,CAAC,MAAuB;IAClD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,KAAgB,EAAE,MAAe;IAC9D,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,OAAO;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAgC,CAAC;IAC1C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { redactFields } from './redact';
2
+ export { getTimestamp, formatLogEntry, serializeError } from './format';
3
+ export { extractContext, generateRequestId } from './extract-context';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { redactFields } from './redact';
2
+ export { getTimestamp, formatLogEntry, serializeError } from './format';
3
+ export { extractContext, generateRequestId } from './extract-context';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;AAAA,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AACrB,mCAAwE;AAA/D,sGAAA,YAAY,OAAA;AAAE,wGAAA,cAAc,OAAA;AAAE,wGAAA,cAAc,OAAA;AACrD,qDAAsE;AAA7D,iHAAA,cAAc,OAAA;AAAE,oHAAA,iBAAiB,OAAA"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Recursively redacts sensitive fields from an object.
3
+ * Creates a new object without mutating the original.
4
+ *
5
+ * @param data - The object to redact
6
+ * @param paths - Array of field names to redact (case-insensitive matching)
7
+ * @returns A new object with sensitive fields replaced by '[REDACTED]'
8
+ */
9
+ export declare function redactFields(data: Record<string, unknown>, paths: string[]): Record<string, unknown>;
10
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/utils/redact.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EAAE,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB"}
@@ -0,0 +1,40 @@
1
+ import { REDACTED_VALUE } from '../constants';
2
+ /**
3
+ * Recursively redacts sensitive fields from an object.
4
+ * Creates a new object without mutating the original.
5
+ *
6
+ * @param data - The object to redact
7
+ * @param paths - Array of field names to redact (case-insensitive matching)
8
+ * @returns A new object with sensitive fields replaced by '[REDACTED]'
9
+ */
10
+ export function redactFields(data, paths) {
11
+ if (!data || typeof data !== 'object' || paths.length === 0) {
12
+ return data;
13
+ }
14
+ const lowerPaths = paths.map((p) => p.toLowerCase());
15
+ return redactRecursive(data, lowerPaths);
16
+ }
17
+ function redactRecursive(obj, lowerPaths) {
18
+ if (obj === null || obj === undefined) {
19
+ return obj;
20
+ }
21
+ if (Array.isArray(obj)) {
22
+ return obj.map((item) => redactRecursive(item, lowerPaths));
23
+ }
24
+ if (typeof obj !== 'object') {
25
+ return obj;
26
+ }
27
+ const result = {};
28
+ for (const [key, value] of Object.entries(obj)) {
29
+ if (lowerPaths.includes(key.toLowerCase())) {
30
+ result[key] = REDACTED_VALUE;
31
+ }
32
+ else if (value !== null && typeof value === 'object') {
33
+ result[key] = redactRecursive(value, lowerPaths);
34
+ }
35
+ else {
36
+ result[key] = value;
37
+ }
38
+ }
39
+ return result;
40
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/utils/redact.ts"],"names":[],"mappings":";;AAUA,oCAWC;AArBD,4CAA8C;AAE9C;;;;;;;GAOG;AACH,SAAgB,YAAY,CAC1B,IAA6B,EAC7B,KAAe;IAEf,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAErD,OAAO,eAAe,CAAC,IAAI,EAAE,UAAU,CAA4B,CAAC;AACtE,CAAC;AAED,SAAS,eAAe,CACtB,GAAY,EACZ,UAAoB;IAEpB,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAA8B,CAAC,EAAE,CAAC;QAC1E,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC3C,MAAM,CAAC,GAAG,CAAC,GAAG,0BAAc,CAAC;QAC/B,CAAC;aAAM,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvD,MAAM,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "serverless-event-logger",
3
+ "version": "1.0.0",
4
+ "description": "🚀 Lightweight, zero-dependency structured JSON logger for AWS Lambda. Optimized for observability with automatic context extraction from serverless events.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/index.js",
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc && npm run build:esm",
20
+ "build:esm": "tsc -p tsconfig.esm.json",
21
+ "test": "jest",
22
+ "test:coverage": "jest --coverage",
23
+ "prepublishOnly": "npm run build && npm test",
24
+ "lint": "eslint src --ext .ts",
25
+ "clean": "rm -rf dist"
26
+ },
27
+ "keywords": [
28
+ "logging",
29
+ "logger",
30
+ "lambda",
31
+ "aws",
32
+ "cloudwatch",
33
+ "json",
34
+ "structured-logging",
35
+ "serverless",
36
+ "observability"
37
+ ],
38
+ "author": "MLHolding",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/mlholding/serverless-event-logger.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/mlholding/serverless-event-logger/issues"
46
+ },
47
+ "homepage": "https://github.com/mlholding/serverless-event-logger#readme",
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/jest": "^29.5.12",
53
+ "@types/node": "^20.11.0",
54
+ "jest": "^29.7.0",
55
+ "ts-jest": "^29.1.2",
56
+ "typescript": "^5.3.3"
57
+ },
58
+ "peerDependencies": {
59
+ "serverless-event-orchestrator": "^1.2.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "serverless-event-orchestrator": {
63
+ "optional": true
64
+ }
65
+ }
66
+ }