@arivlabs/logger 1.5.0 → 2.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/CHANGELOG.md +70 -0
- package/README.md +238 -86
- package/dist/index.d.ts +247 -68
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +392 -82
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +237 -93
- package/dist/index.test.js.map +1 -1
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,76 @@ All notable changes to `@arivlabs/logger` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.0] - 2026-01-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Async logging mode** (default in production) for high-throughput, non-blocking logging
|
|
13
|
+
- Configurable via `enableAsync: true/false` option
|
|
14
|
+
- `asyncBufferSize` option to control buffer size (default: 4096 bytes)
|
|
15
|
+
- Automatic sync mode for development, local, and test environments
|
|
16
|
+
- **Crash-safe logging** with synchronous flush for guaranteed delivery on exceptions
|
|
17
|
+
- Opt-in via `handleExceptions: true` config option
|
|
18
|
+
- Uses SonicBoom's `flushSync()` to ensure logs are written before process exit
|
|
19
|
+
- **Graceful shutdown API**
|
|
20
|
+
- `logger.flush()` - Synchronously flush buffered logs
|
|
21
|
+
- `logger.shutdown()` - Flush and close destinations (call before process exit)
|
|
22
|
+
- **Flexible types** - Define your own service/domain types locally, no more package updates
|
|
23
|
+
- **Custom base fields** - Add fields to every log via `base` config option
|
|
24
|
+
- **Direct pino access** - `logger.pino` property for advanced use cases
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **BREAKING**: Upgraded Pino from v9.6.0 to v10.2.0
|
|
29
|
+
- Drops Node.js 18 support (already required Node >=20)
|
|
30
|
+
- Fixes memory leak when using transports with `--import preload`
|
|
31
|
+
- **BREAKING**: Async logging is now default in production environments
|
|
32
|
+
- **Action required**: Add `await logger.shutdown()` to your SIGTERM handlers
|
|
33
|
+
- Use `enableAsync: false` to opt out if needed
|
|
34
|
+
- **BREAKING**: Exception handling is now opt-in via `handleExceptions: true`
|
|
35
|
+
- Previously registered handlers automatically; now explicit for safety
|
|
36
|
+
- Simplified API: removed generic type parameters (use TypeScript's type inference)
|
|
37
|
+
- Improved flush/shutdown reliability using proper SonicBoom methods
|
|
38
|
+
|
|
39
|
+
### Removed
|
|
40
|
+
|
|
41
|
+
- **BREAKING**: `ServiceName` type - Define your own service types locally
|
|
42
|
+
- **BREAKING**: `LogDomain` type - Define your own domain types locally
|
|
43
|
+
- All hard-coded service and domain names removed from the package
|
|
44
|
+
|
|
45
|
+
### Deprecated
|
|
46
|
+
|
|
47
|
+
- `createDomainLogger()` function - Use `logger.domain()` instead
|
|
48
|
+
- `createRequestLogger()` function - Use `logger.withContext()` instead
|
|
49
|
+
|
|
50
|
+
### Migration Guide
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// 1. Create logger (service/domain are now plain strings)
|
|
54
|
+
const logger = createLogger({ service: 'my-service' });
|
|
55
|
+
|
|
56
|
+
// 2. Add shutdown handler (required for async mode in production)
|
|
57
|
+
process.on('SIGTERM', async () => {
|
|
58
|
+
await logger.shutdown();
|
|
59
|
+
process.exit(0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 3. (Optional) Enable exception handling
|
|
63
|
+
const logger = createLogger({
|
|
64
|
+
service: 'my-service',
|
|
65
|
+
handleExceptions: true, // Opt-in for crash-safe logging
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 4. (Optional) Opt out of async mode
|
|
69
|
+
const logger = createLogger({ service: 'my-service', enableAsync: false });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## [1.5.0] - 2025-01-10
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- Added `feature-flags` to `LogDomain` type
|
|
77
|
+
|
|
8
78
|
## [1.4.1] - 2024-12-29
|
|
9
79
|
|
|
10
80
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# @arivlabs/logger
|
|
2
2
|
|
|
3
|
-
Structured logging for
|
|
3
|
+
Structured, high-performance logging for Node.js services with CloudWatch support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Async by default in production** - Non-blocking logging for maximum throughput
|
|
8
|
+
- **Crash-safe logging** - Uses `pino.final()` for guaranteed delivery on exceptions
|
|
9
|
+
- **Flexible types** - Define your own service and domain types locally
|
|
10
|
+
- **Automatic redaction** - Sensitive data masked by default
|
|
11
|
+
- **Child loggers** - Domain-specific and request-scoped logging
|
|
12
|
+
- **CloudWatch-friendly** - JSON output optimized for AWS CloudWatch Insights
|
|
13
|
+
- **Pino v10** - Built on the fastest Node.js logger
|
|
4
14
|
|
|
5
15
|
## Installation
|
|
6
16
|
|
|
@@ -11,88 +21,142 @@ pnpm add @arivlabs/logger
|
|
|
11
21
|
pnpm add -D pino-pretty
|
|
12
22
|
```
|
|
13
23
|
|
|
14
|
-
##
|
|
24
|
+
## Quick Start
|
|
15
25
|
|
|
16
26
|
```typescript
|
|
17
27
|
import { createLogger } from '@arivlabs/logger';
|
|
18
28
|
|
|
19
|
-
|
|
20
|
-
const logger = createLogger({ service: 'api-gateway' });
|
|
29
|
+
const logger = createLogger({ service: 'my-service' });
|
|
21
30
|
|
|
22
|
-
// Basic logging
|
|
31
|
+
// Basic logging (intuitive style)
|
|
23
32
|
logger.info('Server started', { port: 3000 });
|
|
24
33
|
|
|
25
34
|
// Error logging - both { err } and { error } work
|
|
26
|
-
logger.error('Request failed', { err: error });
|
|
27
|
-
logger.error('Request failed', { error
|
|
28
|
-
// logger.error('Request failed', { error: err.message }); // ❌ Bad - pass the Error object, not .message
|
|
29
|
-
|
|
30
|
-
// Also works: pino native style
|
|
31
|
-
logger.info({ msg: 'Server started', port: 3000 });
|
|
35
|
+
logger.error('Request failed', { err: error });
|
|
36
|
+
logger.error('Request failed', { error }); // Also works (auto-converted)
|
|
32
37
|
|
|
33
38
|
// Domain-specific logging
|
|
34
|
-
const
|
|
35
|
-
|
|
39
|
+
const authLogger = logger.domain('auth');
|
|
40
|
+
authLogger.info('User logged in', { userId: '123' });
|
|
36
41
|
|
|
37
42
|
// Request context logging
|
|
38
|
-
const
|
|
43
|
+
const reqLogger = logger.withContext({
|
|
39
44
|
correlationId: 'abc-123',
|
|
40
45
|
tenantId: 'tenant-1',
|
|
41
46
|
domain: 'discovery',
|
|
42
47
|
});
|
|
43
|
-
|
|
48
|
+
reqLogger.info('Processing request');
|
|
49
|
+
|
|
50
|
+
// IMPORTANT: Graceful shutdown (required for async mode)
|
|
51
|
+
process.on('SIGTERM', async () => {
|
|
52
|
+
await logger.shutdown();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
});
|
|
44
55
|
```
|
|
45
56
|
|
|
46
|
-
##
|
|
57
|
+
## Async Logging
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
-- Filter by service
|
|
50
|
-
fields @timestamp, @message
|
|
51
|
-
| filter service = "api-gateway"
|
|
59
|
+
By default, the logger uses **async mode in production** for high throughput:
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
```typescript
|
|
62
|
+
const logger = createLogger({
|
|
63
|
+
service: 'my-service',
|
|
64
|
+
// enableAsync: true is default in production
|
|
65
|
+
});
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| filter level = "error"
|
|
67
|
+
// Logs are buffered and written asynchronously
|
|
68
|
+
logger.info('High volume log', { requestId: '123' });
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
// CRITICAL: Always flush on shutdown!
|
|
71
|
+
process.on('SIGTERM', async () => {
|
|
72
|
+
await logger.shutdown();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
77
|
+
### Async Defaults by Environment
|
|
78
|
+
|
|
79
|
+
| Environment | `enableAsync` Default | Why |
|
|
80
|
+
| ----------- | --------------------- | ----------------------------- |
|
|
81
|
+
| production | `true` | High throughput, non-blocking |
|
|
82
|
+
| development | `false` | Immediate feedback during dev |
|
|
83
|
+
| local | `false` | Immediate feedback |
|
|
84
|
+
| test | `false` | Predictable test output |
|
|
85
|
+
|
|
86
|
+
### Disabling Async Mode
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const logger = createLogger({
|
|
90
|
+
service: 'my-service',
|
|
91
|
+
enableAsync: false, // All logs written synchronously
|
|
92
|
+
});
|
|
70
93
|
```
|
|
71
94
|
|
|
95
|
+
### Async Configuration
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const logger = createLogger({
|
|
99
|
+
service: 'my-service',
|
|
100
|
+
enableAsync: true,
|
|
101
|
+
asyncBufferSize: 4096, // Buffer size before auto-flush (default: 4096)
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Exception Handling (Opt-in)
|
|
106
|
+
|
|
107
|
+
For crash-safe logging of uncaught exceptions, enable `handleExceptions`:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const logger = createLogger({
|
|
111
|
+
service: 'my-service',
|
|
112
|
+
handleExceptions: true, // Registers uncaughtException/unhandledRejection handlers
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
When enabled, the logger:
|
|
117
|
+
|
|
118
|
+
1. Logs the error at `fatal` level
|
|
119
|
+
2. Calls `flushSync()` on the SonicBoom destination to ensure the log is written
|
|
120
|
+
3. Exits the process with code 1
|
|
121
|
+
|
|
122
|
+
**Note:** This is opt-in because automatic process exit behavior may not be desired in all applications.
|
|
123
|
+
|
|
72
124
|
## Configuration
|
|
73
125
|
|
|
74
126
|
```typescript
|
|
75
127
|
const logger = createLogger({
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
128
|
+
// Required
|
|
129
|
+
service: 'my-service',
|
|
130
|
+
|
|
131
|
+
// Optional
|
|
132
|
+
environment: 'production', // defaults to ENV or NODE_ENV
|
|
133
|
+
level: 'info', // defaults to 'debug' in dev, 'info' in prod
|
|
134
|
+
pretty: false, // defaults to true in development/local
|
|
135
|
+
enableAsync: true, // defaults to true in production
|
|
136
|
+
asyncBufferSize: 4096, // buffer size for async mode
|
|
137
|
+
handleExceptions: false, // opt-in for crash-safe logging
|
|
138
|
+
|
|
139
|
+
// Custom base fields (added to every log)
|
|
140
|
+
base: {
|
|
141
|
+
version: '2.0.0',
|
|
142
|
+
region: 'us-east-1',
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// Custom redaction paths (in addition to defaults)
|
|
80
146
|
redact: {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
147
|
+
paths: ['user.ssn', 'payment.cardNumber'],
|
|
148
|
+
censor: '[MASKED]', // default: '[REDACTED]'
|
|
149
|
+
remove: false, // set true to remove key entirely
|
|
84
150
|
},
|
|
85
151
|
});
|
|
86
152
|
```
|
|
87
153
|
|
|
88
154
|
## Sensitive Data Redaction
|
|
89
155
|
|
|
90
|
-
The logger automatically
|
|
156
|
+
The logger automatically masks common sensitive fields:
|
|
91
157
|
|
|
92
158
|
### Default Redacted Fields
|
|
93
159
|
|
|
94
|
-
These fields are **automatically masked** (shown as `[REDACTED]`):
|
|
95
|
-
|
|
96
160
|
| Category | Fields |
|
|
97
161
|
| ------------------- | ----------------------------------------------------- |
|
|
98
162
|
| **Secrets** | `password`, `secret`, `token`, `apiKey`, `privateKey` |
|
|
@@ -101,63 +165,76 @@ These fields are **automatically masked** (shown as `[REDACTED]`):
|
|
|
101
165
|
| **Request Headers** | `req.headers.authorization`, `req.headers.cookie` |
|
|
102
166
|
| **Nested** | `*.password`, `*.secret`, `*.token`, `*.apiKey` |
|
|
103
167
|
|
|
104
|
-
### Adding Custom Redaction
|
|
168
|
+
### Adding Custom Redaction
|
|
105
169
|
|
|
106
170
|
```typescript
|
|
107
171
|
const logger = createLogger({
|
|
108
|
-
service: '
|
|
172
|
+
service: 'my-service',
|
|
109
173
|
redact: {
|
|
110
|
-
|
|
111
|
-
paths: [
|
|
112
|
-
'user.ssn', // Exact path
|
|
113
|
-
'customer.creditCard', // Exact path
|
|
114
|
-
'*.bankAccount', // Any object with bankAccount
|
|
115
|
-
],
|
|
116
|
-
censor: '[MASKED]', // Custom mask text
|
|
117
|
-
remove: false, // Set true to remove key entirely
|
|
174
|
+
paths: ['user.ssn', 'payment.cardNumber', '*.bankAccount'],
|
|
118
175
|
},
|
|
119
176
|
});
|
|
120
177
|
```
|
|
121
178
|
|
|
122
|
-
## Log Format
|
|
179
|
+
## Log Output Format
|
|
123
180
|
|
|
124
|
-
JSON
|
|
181
|
+
**JSON (production):**
|
|
125
182
|
|
|
126
183
|
```json
|
|
127
184
|
{
|
|
128
185
|
"level": 30,
|
|
129
|
-
"timestamp": "
|
|
130
|
-
"service": "
|
|
186
|
+
"timestamp": "2026-01-21T10:30:00.000Z",
|
|
187
|
+
"service": "my-service",
|
|
131
188
|
"environment": "production",
|
|
132
|
-
"domain": "
|
|
189
|
+
"domain": "auth",
|
|
133
190
|
"correlation_id": "abc-123",
|
|
134
191
|
"tenant_id": "tenant-1",
|
|
135
|
-
"msg": "
|
|
136
|
-
"
|
|
192
|
+
"msg": "User logged in",
|
|
193
|
+
"userId": "user-456"
|
|
137
194
|
}
|
|
138
195
|
```
|
|
139
196
|
|
|
140
|
-
Pretty
|
|
197
|
+
**Pretty (development):**
|
|
141
198
|
|
|
142
199
|
```
|
|
143
|
-
10:30:00 Z [
|
|
200
|
+
10:30:00 Z [my-service:auth] abc-123 User logged in
|
|
144
201
|
```
|
|
145
202
|
|
|
146
|
-
##
|
|
203
|
+
## CloudWatch Insights Queries
|
|
204
|
+
|
|
205
|
+
```sql
|
|
206
|
+
-- Filter by service
|
|
207
|
+
fields @timestamp, @message | filter service = "my-service"
|
|
208
|
+
|
|
209
|
+
-- Filter by domain
|
|
210
|
+
fields @timestamp, @message | filter domain = "auth"
|
|
147
211
|
|
|
148
|
-
|
|
212
|
+
-- Filter errors (level 50 = error)
|
|
213
|
+
fields @timestamp, @message | filter level >= 50
|
|
214
|
+
|
|
215
|
+
-- Filter by tenant
|
|
216
|
+
fields @timestamp, @message | filter tenant_id = "tenant-123"
|
|
217
|
+
|
|
218
|
+
-- Trace a request
|
|
219
|
+
fields @timestamp, service, domain, @message
|
|
220
|
+
| filter correlation_id = "abc-123"
|
|
221
|
+
| sort @timestamp asc
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Error Logging
|
|
225
|
+
|
|
226
|
+
Pass the Error object directly:
|
|
149
227
|
|
|
150
228
|
```typescript
|
|
151
229
|
try {
|
|
152
230
|
await someOperation();
|
|
153
231
|
} catch (error) {
|
|
154
|
-
//
|
|
232
|
+
// Both work - error is auto-converted to err
|
|
155
233
|
logger.error('Operation failed', { err: error });
|
|
156
234
|
logger.error('Operation failed', { error }); // Same result
|
|
157
235
|
|
|
158
|
-
//
|
|
159
|
-
logger.error('Operation failed', {
|
|
160
|
-
logger.error('Operation failed', { message: error.message, stack: error.stack });
|
|
236
|
+
// Bad - loses error type, stack, and custom properties
|
|
237
|
+
logger.error('Operation failed', { message: error.message });
|
|
161
238
|
}
|
|
162
239
|
```
|
|
163
240
|
|
|
@@ -165,22 +242,97 @@ Pino's error serializer captures:
|
|
|
165
242
|
|
|
166
243
|
- Error name/type (e.g., `TypeError`, `ValidationError`)
|
|
167
244
|
- Error message
|
|
168
|
-
-
|
|
245
|
+
- Full stack trace
|
|
169
246
|
- Custom error properties
|
|
170
247
|
|
|
171
|
-
##
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
248
|
+
## API Reference
|
|
249
|
+
|
|
250
|
+
### `createLogger(config)`
|
|
251
|
+
|
|
252
|
+
Creates a new logger instance.
|
|
253
|
+
|
|
254
|
+
### `ArivLogger` Interface
|
|
255
|
+
|
|
256
|
+
| Method | Description |
|
|
257
|
+
| ----------------------- | ------------------------------------------ |
|
|
258
|
+
| `trace(msg, data?)` | Log at trace level |
|
|
259
|
+
| `debug(msg, data?)` | Log at debug level |
|
|
260
|
+
| `info(msg, data?)` | Log at info level |
|
|
261
|
+
| `warn(msg, data?)` | Log at warn level |
|
|
262
|
+
| `error(msg, data?)` | Log at error level |
|
|
263
|
+
| `fatal(msg, data?)` | Log at fatal level |
|
|
264
|
+
| `domain(name)` | Create child logger for domain |
|
|
265
|
+
| `withContext(ctx)` | Create child logger with request context |
|
|
266
|
+
| `child(bindings)` | Create child logger with custom bindings |
|
|
267
|
+
| `isLevelEnabled(level)` | Check if level is enabled |
|
|
268
|
+
| `flush()` | Synchronously flush buffered logs |
|
|
269
|
+
| `shutdown()` | Flush and close (call before process exit) |
|
|
270
|
+
| `pino` | Access underlying Pino logger |
|
|
271
|
+
|
|
272
|
+
## Migration from v1.x
|
|
273
|
+
|
|
274
|
+
### Breaking Changes
|
|
275
|
+
|
|
276
|
+
1. **Async logging is now default in production**
|
|
277
|
+
- Add shutdown handler: `await logger.shutdown()`
|
|
278
|
+
|
|
279
|
+
2. **`ServiceName` and `LogDomain` types removed**
|
|
280
|
+
- Define your own types locally
|
|
281
|
+
|
|
282
|
+
3. **Config option renamed**: `async` → `enableAsync`
|
|
283
|
+
|
|
284
|
+
4. **Exception handling is now opt-in**
|
|
285
|
+
- Use `handleExceptions: true` if needed
|
|
286
|
+
|
|
287
|
+
### Migration Steps
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Before (v1.x)
|
|
291
|
+
const logger = createLogger({ service: 'api-gateway' });
|
|
292
|
+
|
|
293
|
+
// After (v2.x)
|
|
294
|
+
const logger = createLogger({ service: 'my-service' });
|
|
295
|
+
|
|
296
|
+
// Add shutdown handler (required for async mode)
|
|
297
|
+
process.on('SIGTERM', async () => {
|
|
298
|
+
await logger.shutdown();
|
|
299
|
+
process.exit(0);
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Performance Tips
|
|
304
|
+
|
|
305
|
+
1. **Use `isLevelEnabled` for expensive computations:**
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
if (logger.isLevelEnabled('debug')) {
|
|
309
|
+
logger.debug('Details', { data: computeExpensiveData() });
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
2. **Keep log messages short** - data goes in the object:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// Good
|
|
317
|
+
logger.info('Request processed', { userId, duration, status });
|
|
318
|
+
|
|
319
|
+
// Bad
|
|
320
|
+
logger.info(`Request for user ${userId} took ${duration}ms with status ${status}`);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
3. **Reuse domain loggers:**
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// Good - create once, reuse
|
|
327
|
+
const authLogger = logger.domain('auth');
|
|
328
|
+
authLogger.info('Login');
|
|
329
|
+
authLogger.info('Logout');
|
|
330
|
+
|
|
331
|
+
// Bad - wasteful
|
|
332
|
+
logger.domain('auth').info('Login');
|
|
333
|
+
logger.domain('auth').info('Logout');
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## License
|
|
337
|
+
|
|
338
|
+
MIT
|