scribelog 1.0.5 → 1.1.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 +227 -241
- package/dist/format.d.ts +6 -0
- package/dist/format.js +78 -21
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +14 -16
- package/dist/logger.js +107 -75
- package/dist/logger.js.map +1 -1
- package/dist/transports/file.d.ts +14 -0
- package/dist/transports/file.js +165 -0
- package/dist/transports/file.js.map +1 -0
- package/dist/types.d.ts +31 -17
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
# Scribelog 🪵📝
|
|
2
3
|
|
|
3
4
|
[](https://www.npmjs.com/package/scribelog)
|
|
@@ -5,20 +6,23 @@
|
|
|
5
6
|
[](https://github.com/tolongames/scribelog/actions/workflows/node.js.yml)
|
|
6
7
|
<!-- Add other badges if you have them (e.g., coverage) -->
|
|
7
8
|
|
|
8
|
-
**Scribelog** is an advanced, highly configurable logging library for Node.js applications, written in TypeScript. It offers flexible formatting, support for multiple destinations (transports), child loggers,
|
|
9
|
+
**Scribelog** is an advanced, highly configurable logging library for Node.js applications, written in TypeScript. It offers flexible formatting, support for multiple destinations (transports), child loggers, automatic error catching, and printf-style interpolation, aiming for a great developer experience.
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
## ✨ Key Features
|
|
13
14
|
|
|
14
15
|
* **Standard Logging Levels:** Uses familiar levels (`error`, `warn`, `info`, `http`, `verbose`, `debug`, `silly`).
|
|
15
|
-
* **Highly Flexible Formatting:** Combine powerful formatters (`simple`, `json`, `timestamp`, `metadata`, `errors`, etc.) using a composable API (`format.combine`). Customize timestamps, include/exclude metadata, and more.
|
|
16
|
+
* **Highly Flexible Formatting:** Combine powerful formatters (`simple`, `json`, `timestamp`, `metadata`, `errors`, `splat`, etc.) using a composable API (`format.combine`). Customize timestamps, include/exclude metadata, and more.
|
|
17
|
+
* **Printf-Style Logging:** Use `printf`-like placeholders (`%s`, `%d`, `%j`) for easy message interpolation.
|
|
16
18
|
* **Console Color Support:** Automatic, readable colorization for the `simple` format in TTY environments.
|
|
17
|
-
* **Multiple Transports:** Log to different destinations.
|
|
19
|
+
* **Multiple Transports:** Log to different destinations. Built-in `ConsoleTransport` and `FileTransport` with rotation options.
|
|
18
20
|
* **Child Loggers:** Easily create contextual loggers (`logger.child({...})`) that inherit settings but add specific metadata (like `requestId`).
|
|
19
21
|
* **Automatic Error Handling:** Optionally catch and log `uncaughtException` and `unhandledRejection` events, including stack traces.
|
|
20
22
|
* **TypeScript First:** Written entirely in TypeScript for type safety and excellent editor autocompletion.
|
|
21
23
|
|
|
24
|
+
**Explore all features in the [Full Documentation](./DOCUMENTATION.md)!** ⬅
|
|
25
|
+
|
|
22
26
|
---
|
|
23
27
|
|
|
24
28
|
## 📦 Installation
|
|
@@ -45,47 +49,51 @@ import { createLogger, format, transports } from 'scribelog';
|
|
|
45
49
|
// - Transport: Console
|
|
46
50
|
const logger = createLogger();
|
|
47
51
|
|
|
48
|
-
//
|
|
52
|
+
// Standard logging
|
|
49
53
|
logger.info('Application started successfully.');
|
|
50
|
-
logger.warn('Warning: Cache memory usage high.', { usage: '85%' }); //
|
|
51
|
-
|
|
52
|
-
// --- Correct way to log Errors ---
|
|
53
|
-
// Pass a message string as the first argument,
|
|
54
|
-
// and the Error object in the metadata (typically under the 'error' key).
|
|
55
|
-
// The `format.errors()` formatter (included in defaults) will handle it.
|
|
56
|
-
const dbError = new Error('Database connection timeout');
|
|
57
|
-
(dbError as any).code = 'DB_TIMEOUT'; // You can add custom properties to errors
|
|
58
|
-
logger.error('Database Error Occurred', { error: dbError });
|
|
54
|
+
logger.warn('Warning: Cache memory usage high.', { usage: '85%' }); // With metadata
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
// Printf-style formatting (using the default format's built-in splat())
|
|
57
|
+
const username = 'Alice';
|
|
58
|
+
const userId = 123;
|
|
59
|
+
logger.info('User %s (ID: %d) logged in.', username, userId);
|
|
61
60
|
|
|
62
|
-
//
|
|
63
|
-
|
|
61
|
+
// Correct way to log Errors (pass error object in metadata)
|
|
62
|
+
const dbError = new Error('Database connection timeout');
|
|
63
|
+
(dbError as any).code = 'DB_TIMEOUT'; // Add custom properties
|
|
64
|
+
logger.error('Database Error Occurred', { error: dbError }); // format.errors() will process this
|
|
64
65
|
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
level: 'debug', // Log
|
|
68
|
-
|
|
66
|
+
// Log to a file in JSON format
|
|
67
|
+
const fileLogger = createLogger({
|
|
68
|
+
level: 'debug', // Log more details to the file
|
|
69
|
+
transports: [
|
|
70
|
+
new transports.File({
|
|
71
|
+
filename: 'app-%DATE%.log', // Filename pattern (date-fns)
|
|
72
|
+
interval: '1d', // Rotate daily
|
|
73
|
+
path: './logs', // Store logs in a 'logs' subfolder
|
|
74
|
+
compress: 'gzip', // Compress rotated files
|
|
75
|
+
maxFiles: 7, // Keep 7 days of logs
|
|
76
|
+
format: format.defaultJsonFormat // Log as JSON to the file
|
|
77
|
+
})
|
|
78
|
+
]
|
|
69
79
|
});
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// {"level":"debug","message":"Debugging operation X","timestamp":"...ISO_STRING...","operationId":"op-xyz"}
|
|
81
|
+
fileLogger.debug('Writing detailed debug log to file.', { data: { complex: true }});
|
|
82
|
+
fileLogger.info('User action logged to file.', { userId: 456 });
|
|
74
83
|
```
|
|
75
84
|
|
|
76
|
-
**Example Output (Default Simple Format with Colors):**
|
|
85
|
+
**Example Console Output (Default Simple Format with Colors):**
|
|
77
86
|
|
|
78
87
|
```bash
|
|
79
|
-
# (
|
|
88
|
+
# (Timestamps gray, Levels colored, Metadata inspected)
|
|
80
89
|
2024-05-01T10:00:00.123Z [INFO]: Application started successfully.
|
|
81
90
|
2024-05-01T10:00:01.456Z [WARN]: Warning: Cache memory usage high. { usage: '85%' }
|
|
91
|
+
2024-05-01T10:00:01.890Z [INFO]: User Alice (ID: 123) logged in.
|
|
82
92
|
2024-05-01T10:00:02.789Z [ERROR]: Database connection timeout { exception: true, eventType: undefined, errorName: 'Error', code: 'DB_TIMEOUT' }
|
|
83
93
|
Error: Database connection timeout
|
|
84
94
|
at <anonymous>:10:17
|
|
85
95
|
... (stack trace) ...
|
|
86
|
-
2024-05-01T10:00:03.111Z [INFO]: Operation completed { user: 'admin', durationMs: 120 }
|
|
87
96
|
```
|
|
88
|
-
*(Note: `eventType` is undefined here because the error wasn't logged via `handleExceptions`/`handleRejections`)*
|
|
89
97
|
|
|
90
98
|
---
|
|
91
99
|
|
|
@@ -105,180 +113,182 @@ Create and configure your logger using `createLogger(options?: LoggerOptions)`.
|
|
|
105
113
|
| `handleRejections` | `boolean` | `false` | Catch and log `unhandledRejection` events. |
|
|
106
114
|
| `exitOnError` | `boolean` | `true` | If handling exceptions/rejections, exit process (`process.exit(1)`) after logging. |
|
|
107
115
|
|
|
108
|
-
**Example:
|
|
116
|
+
**Example: Advanced Configuration**
|
|
109
117
|
|
|
110
118
|
```ts
|
|
111
|
-
import { createLogger, format, transports } from 'scribelog';
|
|
119
|
+
import { createLogger, format, transports, LogLevel } from 'scribelog';
|
|
112
120
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
// Helper function to safely get log level from environment
|
|
122
|
+
function getLogLevel(): LogLevel { /* ... (implementation from previous example) ... */ }
|
|
123
|
+
|
|
124
|
+
const advancedLogger = createLogger({
|
|
125
|
+
level: getLogLevel(),
|
|
126
|
+
format: format.combine( // Custom format pipeline
|
|
127
|
+
format.errors({ stack: true }),
|
|
128
|
+
format.splat(), // Apply splat formatting early
|
|
129
|
+
format.timestamp({ format: 'isoDateTime' }), // Use date-fns named format
|
|
130
|
+
format.level(),
|
|
131
|
+
format.message(),
|
|
132
|
+
format.metadata({ alias: 'context' }), // Nest metadata
|
|
133
|
+
format.json() // Output as JSON
|
|
134
|
+
),
|
|
116
135
|
transports: [
|
|
136
|
+
// Log info and above to console with simple format
|
|
117
137
|
new transports.Console({
|
|
118
|
-
|
|
138
|
+
level: 'info',
|
|
139
|
+
format: format.defaultSimpleFormat // Override main format
|
|
119
140
|
}),
|
|
120
|
-
//
|
|
141
|
+
// Log everything to a rotating file
|
|
142
|
+
new transports.File({
|
|
143
|
+
filename: '/var/log/my-app/app.log',
|
|
144
|
+
level: 'debug', // Log everything to file
|
|
145
|
+
// format: // Inherits the JSON format from the logger
|
|
146
|
+
size: '10M', // Rotate after 10 MB
|
|
147
|
+
maxFiles: 5, // Keep 5 rotated files
|
|
148
|
+
compress: 'gzip'
|
|
149
|
+
})
|
|
121
150
|
],
|
|
122
151
|
defaultMeta: {
|
|
123
|
-
service: '
|
|
152
|
+
service: 'advanced-service',
|
|
124
153
|
pid: process.pid,
|
|
125
|
-
// You can add more static metadata here
|
|
126
154
|
},
|
|
127
|
-
handleExceptions: true,
|
|
128
|
-
handleRejections: true,
|
|
129
|
-
|
|
155
|
+
handleExceptions: true,
|
|
156
|
+
handleRejections: true,
|
|
157
|
+
exitOnError: false // Don't exit automatically
|
|
130
158
|
});
|
|
131
159
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
throw new Error('Critical configuration error!');
|
|
136
|
-
} catch (error) {
|
|
137
|
-
// Log the caught error correctly
|
|
138
|
-
prodLogger.error('Failed to apply configuration', { error: error as Error });
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Example of an unhandled rejection that would be caught if not caught here
|
|
142
|
-
// Promise.reject('Something failed asynchronously');
|
|
160
|
+
advancedLogger.info('Advanced logger ready.');
|
|
161
|
+
advancedLogger.debug('This goes only to the file transport as JSON.');
|
|
162
|
+
advancedLogger.error('An error occurred!', { error: new Error("Config read failed"), critical: true });
|
|
143
163
|
```
|
|
144
164
|
|
|
145
165
|
---
|
|
146
166
|
|
|
147
167
|
## 📊 Logging Levels
|
|
148
168
|
|
|
149
|
-
|
|
169
|
+
(Content is the same as before - list of levels and explanation)
|
|
150
170
|
|
|
151
171
|
```text
|
|
152
172
|
error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
|
|
153
173
|
```
|
|
154
|
-
|
|
155
|
-
Setting the `level` option filters messages *at or above* the specified severity. `level: 'info'` logs `info`, `warn`, and `error`. `level: 'debug'` logs everything.
|
|
156
|
-
|
|
157
174
|
---
|
|
158
175
|
|
|
159
176
|
## 🎨 Formatting
|
|
160
177
|
|
|
161
|
-
Formatters transform the log `info` object
|
|
178
|
+
Formatters transform the log `info` object. Chain them using `format.combine(...)`. The order matters!
|
|
162
179
|
|
|
163
180
|
**How it Works:**
|
|
164
|
-
`
|
|
181
|
+
`log(msg, ...args)` -> Creates `LogInfo { message, splat?, ... }` -> Passes to `format` -> `combine` applies chain -> Result passed to `transport.log()`.
|
|
165
182
|
|
|
166
183
|
### Available Formatters
|
|
167
184
|
|
|
168
|
-
* **`format.timestamp(options?)`**: Adds/formats a timestamp.
|
|
169
|
-
* `alias?: string
|
|
170
|
-
* `format?: string | ((date: Date) => string)
|
|
171
|
-
```ts
|
|
172
|
-
format.timestamp({ format: 'yyyy-MM-dd HH:mm:ss' }) // -> Adds { timestamp: '2024-05-01 10:30:00' }
|
|
173
|
-
format.timestamp({ alias: '@timestamp' }) // -> Adds { '@timestamp': '...ISO...' }
|
|
174
|
-
```
|
|
185
|
+
* **`format.timestamp(options?)`**: Adds/formats a timestamp (default: ISO).
|
|
186
|
+
* `alias?: string` (default: `'timestamp'`)
|
|
187
|
+
* `format?: string | ((date: Date) => string)` (`date-fns` format or function).
|
|
175
188
|
* **`format.level(options?)`**: Adds the log level string.
|
|
176
|
-
* `alias?: string
|
|
177
|
-
* **`format.message(options?)`**: Adds the log message string.
|
|
178
|
-
* `alias?: string
|
|
179
|
-
* **`format.
|
|
180
|
-
|
|
181
|
-
*
|
|
182
|
-
|
|
183
|
-
* `
|
|
184
|
-
*
|
|
185
|
-
|
|
186
|
-
*
|
|
187
|
-
|
|
189
|
+
* `alias?: string` (default: `'level'`).
|
|
190
|
+
* **`format.message(options?)`**: Adds the log message string (after potential `splat` formatting).
|
|
191
|
+
* `alias?: string` (default: `'message'`).
|
|
192
|
+
* **`format.splat()`**: Interpolates the `message` string using `util.format` and arguments found in `info.splat`. Place **after** `errors()` but **before** `message()` and `metadata()`.
|
|
193
|
+
* **`format.errors(options?)`**: Extracts info from an `Error` object (expected at `info.error`). Adds `errorName`, `stack?`, etc. Place **early** in the chain.
|
|
194
|
+
* `stack?: boolean` (default: `true`).
|
|
195
|
+
* **`format.metadata(options?)`**: Gathers remaining properties. Excludes standard fields (`level`, `message`, `timestamp`, etc.) and error fields.
|
|
196
|
+
* `alias?: string`: Nest metadata under this key.
|
|
197
|
+
* `exclude?: string[]`: Additional keys to exclude.
|
|
198
|
+
* **`format.json(options?)`**: **Terminal.** Serializes `info` to JSON.
|
|
199
|
+
* `space?: string | number`: Pretty-print spaces.
|
|
200
|
+
* **`format.simple(options?)`**: **Terminal.** Creates a human-readable string (colored if TTY). Includes `timestamp`, `level`, `message`, `{ metadata }`, and `stack` (new line).
|
|
201
|
+
* `colors?: boolean`: Force colors (default: auto-detect).
|
|
188
202
|
|
|
189
203
|
### Combining Formatters (`format.combine`)
|
|
190
204
|
|
|
191
|
-
The order matters! Formatters run sequentially. Terminal formatters (`json`, `simple`) should be last.
|
|
192
|
-
|
|
193
205
|
```ts
|
|
194
206
|
import { createLogger, format } from 'scribelog';
|
|
195
207
|
|
|
196
|
-
// Example:
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
format.
|
|
208
|
+
// Example: Simple format with printf-style interpolation first
|
|
209
|
+
const printfSimpleFormat = format.combine(
|
|
210
|
+
format.splat(), // Apply interpolation first
|
|
211
|
+
format.errors({ stack: false }),
|
|
212
|
+
format.timestamp({ format: 'HH:mm:ss' }),
|
|
200
213
|
format.level(),
|
|
201
|
-
format.message(),
|
|
202
|
-
|
|
203
|
-
format.simple()
|
|
214
|
+
format.message(), // Will use the result from splat()
|
|
215
|
+
format.metadata(),
|
|
216
|
+
format.simple()
|
|
204
217
|
);
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
// Output:
|
|
208
|
-
|
|
209
|
-
// Example: JSON output with specific fields and nested metadata
|
|
210
|
-
const customJsonFormat = format.combine(
|
|
211
|
-
format.errors({ stack: false }), // Include basic error info, no stack
|
|
212
|
-
format.timestamp({ alias: '@ts' }), // Rename timestamp field
|
|
213
|
-
format.level({ alias: 'severity' }), // Rename level field
|
|
214
|
-
format.message(), // Keep message field
|
|
215
|
-
format.metadata({ alias: 'data' }), // Nest other data under 'data'
|
|
216
|
-
format.json() // Output as JSON
|
|
217
|
-
);
|
|
218
|
-
const customJsonLogger = createLogger({ format: customJsonFormat });
|
|
219
|
-
customJsonLogger.warn('Warning with nested meta', { user: 'test', id: 1 });
|
|
220
|
-
// Output: {"@ts":"...","severity":"warn","message":"Warning with nested meta","data":{"user":"test","id":1}}
|
|
221
|
-
|
|
222
|
-
const errorExample = new Error("Failed task");
|
|
223
|
-
(errorExample as any).details = { code: 500 };
|
|
224
|
-
customJsonLogger.error("Task failed", { error: errorExample });
|
|
225
|
-
// Output: {"@ts":"...", "severity":"error", "message":"Failed task", "errorName":"Error", "originalReason":undefined, "data":{"details":{"code":500}}}
|
|
218
|
+
const printfLogger = createLogger({ format: printfSimpleFormat });
|
|
219
|
+
printfLogger.info('User %s logged in (ID: %d)', 'Bob', 42, { ip: '127.0.0.1' });
|
|
220
|
+
// Output: 11:22:33 [INFO]: User Bob logged in (ID: 42) { ip: '127.0.0.1' }
|
|
226
221
|
```
|
|
227
222
|
|
|
228
223
|
### Predefined Formats
|
|
229
224
|
|
|
230
|
-
* `format.defaultSimpleFormat`:
|
|
231
|
-
* `format.defaultJsonFormat`:
|
|
225
|
+
* `format.defaultSimpleFormat`: `combine(errors(stack), splat(), timestamp(), level(), message(), metadata(), simple())`. **Default for `createLogger`.**
|
|
226
|
+
* `format.defaultJsonFormat`: `combine(errors(stack), splat(), timestamp(), level(), message(), metadata(), json())`.
|
|
232
227
|
|
|
233
228
|
---
|
|
234
229
|
|
|
235
230
|
## 📤 Transports
|
|
236
231
|
|
|
237
|
-
Define log destinations.
|
|
232
|
+
Define log destinations.
|
|
238
233
|
|
|
239
234
|
### `transports.Console(options?: ConsoleTransportOptions)`
|
|
240
235
|
|
|
241
|
-
Logs to `process.stdout`
|
|
236
|
+
Logs to `process.stdout` / `process.stderr`.
|
|
237
|
+
|
|
238
|
+
* `level?: string`: Transport-specific minimum level.
|
|
239
|
+
* `format?: LogFormat`: Transport-specific format.
|
|
240
|
+
* `useStdErrLevels?: string[]`: Levels to log to `stderr` (default: `['error']`).
|
|
241
|
+
|
|
242
|
+
### `transports.File(options: FileTransportOptions)`
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
* `format?: LogFormat`: Specific format for this transport. Overrides the logger's format.
|
|
245
|
-
* `useStdErrLevels?: string[]`: Array of levels to direct to `stderr` (default: `['error']`).
|
|
244
|
+
Logs to a rotating file using [`rotating-file-stream`](https://github.com/iccicci/rotating-file-stream).
|
|
246
245
|
|
|
247
|
-
|
|
246
|
+
* `filename: string`: Path/name of the log file (required). Can include date patterns like `%DATE%`.
|
|
247
|
+
* `level?: string`: Transport-specific minimum level.
|
|
248
|
+
* `format?: LogFormat`: Transport-specific format (defaults to `format.defaultJsonFormat`).
|
|
249
|
+
* `size?: string`: Max file size before rotation (e.g., `'10M'`).
|
|
250
|
+
* `interval?: string`: Rotation interval (e.g., `'1d'`, `'2h'`).
|
|
251
|
+
* `path?: string`: Directory for rotated/archived files.
|
|
252
|
+
* `compress?: string | boolean`: Compress rotated files (`'gzip'` or `true`).
|
|
253
|
+
* `maxFiles?: number`: Max number of rotated files to keep.
|
|
254
|
+
* `maxSize?: string`: Max total size of all log files.
|
|
255
|
+
* `createPath?: boolean`: Create log directory if it doesn't exist (default: `true`).
|
|
256
|
+
* `fsWriteStreamOptions?: object`: Options passed to `fs.createWriteStream`.
|
|
257
|
+
* *(See `rotating-file-stream` docs for more options like `utc`, generators)*
|
|
258
|
+
|
|
259
|
+
**Example: Logging to Console and File**
|
|
248
260
|
|
|
249
261
|
```ts
|
|
250
262
|
import { createLogger, format, transports } from 'scribelog';
|
|
251
263
|
|
|
252
|
-
const
|
|
253
|
-
level: '
|
|
264
|
+
const fileAndConsoleLogger = createLogger({
|
|
265
|
+
level: 'debug',
|
|
254
266
|
transports: [
|
|
255
|
-
//
|
|
267
|
+
// Console for immediate feedback (info and above)
|
|
256
268
|
new transports.Console({
|
|
257
|
-
level: '
|
|
258
|
-
format: format.simple({ colors: true })
|
|
259
|
-
useStdErrLevels: [], // Nothing from here goes to stderr
|
|
269
|
+
level: 'info',
|
|
270
|
+
format: format.simple({ colors: true })
|
|
260
271
|
}),
|
|
261
|
-
//
|
|
262
|
-
new transports.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
272
|
+
// File for detailed logs (debug and above, JSON)
|
|
273
|
+
new transports.File({
|
|
274
|
+
filename: 'app-debug.log',
|
|
275
|
+
level: 'debug',
|
|
276
|
+
format: format.defaultJsonFormat,
|
|
277
|
+
size: '5M', // Rotate every 5MB
|
|
278
|
+
maxFiles: 3
|
|
266
279
|
})
|
|
267
280
|
]
|
|
268
281
|
});
|
|
269
282
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
logger.debug('Should not appear'); // Filtered out by logger's level ('info')
|
|
283
|
+
fileAndConsoleLogger.debug('Detailed info only in file');
|
|
284
|
+
fileAndConsoleLogger.info('General info in console and file');
|
|
285
|
+
fileAndConsoleLogger.error('Error in console and file', { error: new Error('Failure') });
|
|
274
286
|
```
|
|
275
287
|
|
|
276
288
|
---
|
|
277
289
|
|
|
278
290
|
## 🌱 Child Loggers
|
|
279
291
|
|
|
280
|
-
Create contextual loggers using `logger.child(defaultMeta)`. They inherit settings but automatically add the specified metadata.
|
|
281
|
-
|
|
282
292
|
```ts
|
|
283
293
|
import { createLogger } from 'scribelog';
|
|
284
294
|
|
|
@@ -335,9 +345,11 @@ logger.info('Application running with error handlers.');
|
|
|
335
345
|
|
|
336
346
|
The logger adds `{ exception: true, eventType: '...', ...errorDetails }` to the log metadata for these events, processed by the `format.errors()` formatter. Remember to have `format.errors()` in your format chain to see detailed error info.
|
|
337
347
|
|
|
348
|
+
---
|
|
349
|
+
|
|
338
350
|
## 💻 Showcase
|
|
339
351
|
|
|
340
|
-
You can run this script to see
|
|
352
|
+
You can run this script to see a demonstration of various Scribelog features in action.
|
|
341
353
|
|
|
342
354
|
<details>
|
|
343
355
|
<summary>Click to show/hide showcase.ts code</summary>
|
|
@@ -372,57 +384,54 @@ async function runShowcase() {
|
|
|
372
384
|
|
|
373
385
|
// === 1. Basic Configuration & Levels ===
|
|
374
386
|
console.log('--- 1. Basic Configuration & Levels ---');
|
|
375
|
-
|
|
376
|
-
const logger1 = createLogger({ level: 'debug' }); // Set level to 'debug' to see more logs
|
|
387
|
+
const logger1 = createLogger({ level: 'debug' });
|
|
377
388
|
logger1.info('Logger 1 (level: debug, format: simple)');
|
|
378
389
|
logger1.warn('Warning from Logger 1');
|
|
379
390
|
logger1.debug('Debug message from Logger 1 (should appear)');
|
|
380
|
-
logger1.error('Error from Logger 1');
|
|
391
|
+
logger1.error('Error from Logger 1');
|
|
381
392
|
|
|
382
393
|
// === 2. Different Formats ===
|
|
383
394
|
console.log('\n--- 2. Different Formats ---');
|
|
384
395
|
|
|
385
396
|
// 2a. JSON Format
|
|
386
397
|
console.log('\n--- 2a. JSON Format ---');
|
|
387
|
-
const logger2a = createLogger({
|
|
388
|
-
format: format.defaultJsonFormat, // Use the predefined JSON format (includes errors, timestamp etc.)
|
|
389
|
-
level: 'info' // Set level for this specific logger
|
|
390
|
-
});
|
|
398
|
+
const logger2a = createLogger({ format: format.defaultJsonFormat, level: 'info' });
|
|
391
399
|
logger2a.info('Log in JSON format.', { data: true, value: 123 });
|
|
392
400
|
const jsonError = new Error("JSON Formatted Error");
|
|
393
|
-
(jsonError as any).code = "E_JSON";
|
|
394
|
-
// Log an error object within metadata
|
|
401
|
+
(jsonError as any).code = "E_JSON";
|
|
395
402
|
logger2a.error('An error occurred (JSON)', { error: jsonError, user: 'admin' });
|
|
396
403
|
|
|
397
404
|
// 2b. Custom Simple Format (Colored)
|
|
398
405
|
console.log('\n--- 2b. Custom Simple Format (Colored) ---');
|
|
399
406
|
const customSimpleFormat = format.combine(
|
|
400
|
-
format.timestamp({ format: 'HH:mm:ss' }),
|
|
401
|
-
format.level(),
|
|
402
|
-
format.
|
|
403
|
-
format.
|
|
404
|
-
format.
|
|
405
|
-
format.
|
|
407
|
+
format.timestamp({ format: 'HH:mm:ss' }),
|
|
408
|
+
format.level(),
|
|
409
|
+
format.splat(), // Apply splat formatting
|
|
410
|
+
format.message(),
|
|
411
|
+
format.metadata({ exclude: ['pid'] }),
|
|
412
|
+
format.errors({ stack: false }),
|
|
413
|
+
format.simple({ colors: true })
|
|
406
414
|
);
|
|
407
415
|
const logger2b = createLogger({ format: customSimpleFormat, level: 'info' });
|
|
408
|
-
const originalChalkLevel2b = chalk.level;
|
|
409
|
-
chalk.level = 1; // Force
|
|
410
|
-
logger2b.info('Custom simple format', { pid: 12345, user: 'demo' });
|
|
411
|
-
logger2b.error('Another error (custom simple)', { error: new Error("Simple format error")
|
|
412
|
-
chalk.level = originalChalkLevel2b; // Restore
|
|
416
|
+
const originalChalkLevel2b = chalk.level;
|
|
417
|
+
chalk.level = 1; // Force colors
|
|
418
|
+
logger2b.info('Custom simple format for %s', 'user', { pid: 12345, user: 'demo' }); // Use splat
|
|
419
|
+
logger2b.error('Another error (custom simple)', { error: new Error("Simple format error")});
|
|
420
|
+
chalk.level = originalChalkLevel2b; // Restore
|
|
413
421
|
|
|
414
422
|
// 2c. Custom JSON Format with Aliases and Nesting
|
|
415
423
|
console.log('\n--- 2c. Custom JSON Format (Aliases & Nesting) ---');
|
|
416
424
|
const customJson = format.combine(
|
|
417
|
-
format.
|
|
418
|
-
format.
|
|
419
|
-
format.
|
|
420
|
-
format.
|
|
421
|
-
format.
|
|
422
|
-
format.
|
|
425
|
+
format.errors({ stack: true }), // Handle errors first
|
|
426
|
+
format.splat(), // Then splat
|
|
427
|
+
format.timestamp({ alias: '@timestamp' }),
|
|
428
|
+
format.level({ alias: 'severity' }),
|
|
429
|
+
format.message(),
|
|
430
|
+
format.metadata({ alias: 'details' }),
|
|
431
|
+
format.json()
|
|
423
432
|
);
|
|
424
433
|
const logger2c = createLogger({ format: customJson, level: 'info' });
|
|
425
|
-
logger2c.warn('
|
|
434
|
+
logger2c.warn('Warn %s nested meta', 'with', { transactionId: 'xyz', status: 'WARN' }); // Use splat
|
|
426
435
|
const nestedError = new Error("Nested Error");
|
|
427
436
|
nestedError.stack = "Fake stack\n at place";
|
|
428
437
|
logger2c.error("Error with nested meta", { error: nestedError, code: 503 });
|
|
@@ -430,139 +439,116 @@ async function runShowcase() {
|
|
|
430
439
|
|
|
431
440
|
// === 3. Multiple Transports ===
|
|
432
441
|
console.log('\n--- 3. Multiple Transports ---');
|
|
433
|
-
const originalChalkLevel3 = chalk.level;
|
|
434
|
-
chalk.level = 0; // Disable colors
|
|
442
|
+
const originalChalkLevel3 = chalk.level;
|
|
443
|
+
chalk.level = 0; // Disable colors for comparison
|
|
435
444
|
const logger3 = createLogger({
|
|
436
|
-
level: 'debug',
|
|
445
|
+
level: 'debug',
|
|
437
446
|
transports: [
|
|
438
|
-
// Transport 1: Logs 'info' and above, uses simple format, errors to stderr
|
|
439
447
|
new transports.Console({
|
|
440
|
-
level: 'info',
|
|
441
|
-
format: format.defaultSimpleFormat, //
|
|
442
|
-
useStdErrLevels: ['error']
|
|
448
|
+
level: 'info',
|
|
449
|
+
format: format.defaultSimpleFormat, // Contains splat()
|
|
450
|
+
useStdErrLevels: ['error']
|
|
443
451
|
}),
|
|
444
|
-
// Transport 2: Logs 'debug' and above, uses JSON format, warn/error to stderr
|
|
445
452
|
new transports.Console({
|
|
446
|
-
level: 'debug',
|
|
447
|
-
format: format.defaultJsonFormat, //
|
|
448
|
-
useStdErrLevels: ['warn', 'error']
|
|
453
|
+
level: 'debug',
|
|
454
|
+
format: format.defaultJsonFormat, // Contains splat()
|
|
455
|
+
useStdErrLevels: ['warn', 'error']
|
|
449
456
|
})
|
|
450
457
|
]
|
|
451
458
|
});
|
|
452
|
-
logger3.debug('Debug log
|
|
453
|
-
logger3.info('Info log
|
|
454
|
-
logger3.warn('Warn log (
|
|
455
|
-
logger3.error('Error log (
|
|
456
|
-
chalk.level = originalChalkLevel3; // Restore
|
|
459
|
+
logger3.debug('Debug log: %s', 'JSON only');
|
|
460
|
+
logger3.info('Info log: %s', 'Simple & JSON');
|
|
461
|
+
logger3.warn('Warn log: %s', 'Simple(stdout) & JSON(stderr)');
|
|
462
|
+
logger3.error('Error log: %s', 'Simple(stderr) & JSON(stderr)');
|
|
463
|
+
chalk.level = originalChalkLevel3; // Restore
|
|
457
464
|
|
|
458
|
-
// === 4.
|
|
465
|
+
// === 4. Loggery Potomne (child) ===
|
|
459
466
|
console.log('\n--- 4. Child Loggers ---');
|
|
460
|
-
// Parent logger setup
|
|
461
467
|
const parentLogger = createLogger({
|
|
462
|
-
level: 'debug',
|
|
463
|
-
format: format.simple({ colors: false }), // Use simple
|
|
464
|
-
defaultMeta: { service: 'MainService' }
|
|
468
|
+
level: 'debug',
|
|
469
|
+
format: format.simple({ colors: false }), // Use simple, no colors
|
|
470
|
+
defaultMeta: { service: 'MainService' }
|
|
465
471
|
});
|
|
466
|
-
parentLogger.info('Parent log.');
|
|
467
|
-
|
|
468
|
-
// Create first child, inheriting settings but adding 'module'
|
|
472
|
+
parentLogger.info('Parent log.');
|
|
469
473
|
const childLogger1 = parentLogger.child({ module: 'ModuleA' });
|
|
470
|
-
childLogger1.info('Child 1 log
|
|
471
|
-
|
|
472
|
-
// Create a child of the first child, inheriting and overriding 'module'
|
|
474
|
+
childLogger1.info('Child 1 log for action: %s', 'read'); // Use splat
|
|
473
475
|
const childLogger2 = childLogger1.child({ function: 'doWork', module: 'OverrideModule' });
|
|
474
|
-
childLogger2.debug('Child 2 log (debug).', { value: 42 });
|
|
476
|
+
childLogger2.debug('Child 2 log (debug).', { value: 42 });
|
|
475
477
|
|
|
476
|
-
// === 5. logEntry
|
|
478
|
+
// === 5. logEntry ===
|
|
477
479
|
console.log('\n--- 5. logEntry Method ---');
|
|
478
|
-
// Create a logger specifically for HTTP level logs
|
|
479
480
|
const entryLogger = createLogger({ level: 'http' });
|
|
480
|
-
// Use logEntry to log a pre-structured object.
|
|
481
|
-
// Scribelog will still process it through the format pipeline.
|
|
482
481
|
entryLogger.logEntry({
|
|
483
|
-
level: 'http',
|
|
484
|
-
message: 'Manual
|
|
485
|
-
|
|
486
|
-
method: 'POST',
|
|
487
|
-
url: '/api/users',
|
|
488
|
-
statusCode: 201,
|
|
482
|
+
level: 'http',
|
|
483
|
+
message: 'Manual %s entry with custom data %j', // Add splat placeholders
|
|
484
|
+
splat: ['log', { status: 201 }], // Provide splat data
|
|
485
|
+
method: 'POST', url: '/api/users', statusCode: 201,
|
|
489
486
|
});
|
|
490
|
-
|
|
491
|
-
entryLogger.logEntry({ level: 'info', message: 'This info entry will also show' }); // info > http
|
|
487
|
+
entryLogger.logEntry({ level: 'info', message: 'This info entry will also show' });
|
|
492
488
|
|
|
493
|
-
// === 6.
|
|
489
|
+
// === 6. Obsługa wyjątków i odrzuceń (Symulacja) ===
|
|
490
|
+
// (Keep this section as in the previous example, it doesn't need _internalExit mock)
|
|
494
491
|
console.log('\n--- 6. Exception/Rejection Handling (Simulating real events) ---');
|
|
495
|
-
|
|
496
|
-
// Set to false for this demo to see both handlers potentially log.
|
|
497
|
-
const exitOnHandler = false;
|
|
492
|
+
const handleErrorsAndExit = false;
|
|
498
493
|
let errorHandlingLogger: Logger | undefined = undefined;
|
|
499
494
|
|
|
500
|
-
console.log(`Creating logger with handleExceptions/Rejections, exitOnError: ${
|
|
495
|
+
console.log(`Creating logger with handleExceptions/Rejections, exitOnError: ${handleErrorsAndExit}`);
|
|
501
496
|
errorHandlingLogger = createLogger({
|
|
502
|
-
level: 'debug',
|
|
503
|
-
transports: [new transports.Console({ format: format.defaultSimpleFormat })],
|
|
504
|
-
handleExceptions: true,
|
|
505
|
-
handleRejections: true,
|
|
506
|
-
exitOnError:
|
|
497
|
+
level: 'debug',
|
|
498
|
+
transports: [new transports.Console({ format: format.defaultSimpleFormat })],
|
|
499
|
+
handleExceptions: true,
|
|
500
|
+
handleRejections: true,
|
|
501
|
+
exitOnError: handleErrorsAndExit
|
|
507
502
|
});
|
|
508
|
-
errorHandlingLogger.info(`Error handlers active (exitOnError: ${
|
|
509
|
-
|
|
510
|
-
// --- Simulate Errors ---
|
|
511
|
-
// NOTE: In a real app, these would happen unexpectedly.
|
|
512
|
-
// We use setTimeout to allow the script to reach the end and potentially remove handlers if exitOnError is false.
|
|
503
|
+
errorHandlingLogger.info(`Error handlers active (exitOnError: ${handleErrorsAndExit}).`);
|
|
513
504
|
|
|
514
505
|
console.log("Simulating unhandled rejection (will be logged)...");
|
|
515
|
-
// Intentionally create an unhandled rejection
|
|
516
506
|
Promise.reject("Simulated rejection reason (handled by logger)");
|
|
517
507
|
|
|
518
508
|
console.log("Simulating uncaught exception in 100ms (will be logged)...");
|
|
519
509
|
const exceptionTimer = setTimeout(() => {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
510
|
+
try { throw new Error("Simulated uncaught exception (handled by logger)"); }
|
|
511
|
+
catch (e) { process.emit('uncaughtException', e as Error); }
|
|
512
|
+
if (!handleErrorsAndExit && errorHandlingLogger && typeof (errorHandlingLogger as any).removeExceptionHandlers === 'function') {
|
|
513
|
+
(errorHandlingLogger as any).removeExceptionHandlers();
|
|
514
|
+
console.log("Error handlers removed for no-exit logger.");
|
|
515
|
+
}
|
|
516
|
+
console.log('\n--- Showcase Finished (Error Handlers Tested) ---');
|
|
523
517
|
}, 100);
|
|
524
518
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (errorHandlingLogger && typeof (errorHandlingLogger as any).removeExceptionHandlers === 'function') {
|
|
532
|
-
(errorHandlingLogger as any).removeExceptionHandlers();
|
|
533
|
-
errorHandlingLogger.info('Error handlers removed.'); // Log using the same logger
|
|
534
|
-
} else {
|
|
535
|
-
console.warn('[Showcase] Could not remove error handlers (method not found?).');
|
|
536
|
-
}
|
|
519
|
+
if (!handleErrorsAndExit) {
|
|
520
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
521
|
+
} else {
|
|
522
|
+
// If we expect exit, keep the process running briefly
|
|
523
|
+
// This usually isn't needed as process.exit stops everything
|
|
524
|
+
// await new Promise(resolve => setTimeout(resolve, 1500));
|
|
537
525
|
}
|
|
538
526
|
|
|
539
|
-
// If exitOnError was true, the script might have exited before reaching here.
|
|
540
|
-
console.log('\n===========================================');
|
|
541
|
-
console.log('✅ Scribelog Showcase Finished (Check logs above) ✅');
|
|
542
|
-
console.log('===========================================');
|
|
543
|
-
|
|
544
527
|
} // End runShowcase
|
|
545
528
|
|
|
546
|
-
// Run the main demo function
|
|
547
529
|
runShowcase().catch(e => {
|
|
548
|
-
// This catch is unlikely to be hit if handleExceptions is true,
|
|
549
|
-
// but good practice to have.
|
|
550
530
|
console.error("!!! Unexpected error running showcase:", e);
|
|
551
531
|
});
|
|
552
532
|
```
|
|
553
533
|
|
|
554
534
|
</details>
|
|
535
|
+
|
|
555
536
|
If you have any questions about scribelog or would like to know more about how to use any of the features. You can write to me on discord:
|
|
556
537
|
theonlytolon
|
|
557
538
|
|
|
558
539
|
---
|
|
559
540
|
|
|
541
|
+
## 📘 Documentation
|
|
542
|
+
|
|
543
|
+
For detailed documentation covering all features, configuration options, and advanced examples, please see the [**Full Documentation**](./DOCUMENTATION.md).
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
560
547
|
## 📚 Future Work
|
|
561
548
|
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
* **Async Handling:** Better guarantees for transports finishing writes before `exitOnError`.
|
|
549
|
+
* More built-in formatters (e.g., customizable color themes).
|
|
550
|
+
* Ability to define custom logging levels.
|
|
551
|
+
* Improved handling of asynchronous operations in transports (especially for `exitOnError`).
|
|
566
552
|
|
|
567
553
|
---
|
|
568
554
|
|