scribelog 1.0.5 → 1.1.1
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 +60 -497
- 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,20 +1,21 @@
|
|
|
1
|
+
|
|
1
2
|
# Scribelog 🪵📝
|
|
2
3
|
|
|
3
4
|
[](https://www.npmjs.com/package/scribelog)
|
|
4
5
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/tolongames/scribelog/actions/workflows/node.js.yml)
|
|
6
|
-
<!-- Add other badges if you have them (e.g., coverage) -->
|
|
6
|
+
[](https://github.com/tolongames/scribelog/actions/workflows/node.js.yml) <!-- Zaktualizuj URL, jeśli trzeba -->
|
|
7
7
|
|
|
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,
|
|
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 like Console and File), child loggers, automatic error catching, and printf-style interpolation, aiming for a great developer experience.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
## ✨ Key Features
|
|
13
13
|
|
|
14
14
|
* **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`,
|
|
15
|
+
* **Highly Flexible Formatting:** Combine powerful formatters (`simple`, `json`, `timestamp`, `metadata`, `errors`, `splat`) using a composable API (`format.combine`). Customize outputs easily.
|
|
16
|
+
* **Printf-Style Logging:** Use `printf`-like placeholders (`%s`, `%d`, `%j`) via `format.splat()` for easy message interpolation.
|
|
16
17
|
* **Console Color Support:** Automatic, readable colorization for the `simple` format in TTY environments.
|
|
17
|
-
* **Multiple Transports:** Log to different destinations.
|
|
18
|
+
* **Multiple Transports:** Log to different destinations. Built-in `ConsoleTransport` and `FileTransport` with rotation options.
|
|
18
19
|
* **Child Loggers:** Easily create contextual loggers (`logger.child({...})`) that inherit settings but add specific metadata (like `requestId`).
|
|
19
20
|
* **Automatic Error Handling:** Optionally catch and log `uncaughtException` and `unhandledRejection` events, including stack traces.
|
|
20
21
|
* **TypeScript First:** Written entirely in TypeScript for type safety and excellent editor autocompletion.
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
## 📦 Installation
|
|
25
26
|
|
|
26
27
|
```bash
|
|
28
|
+
# Core library
|
|
27
29
|
npm install scribelog
|
|
28
30
|
# or
|
|
29
31
|
yarn add scribelog
|
|
@@ -33,547 +35,108 @@ pnpm add scribelog
|
|
|
33
35
|
|
|
34
36
|
---
|
|
35
37
|
|
|
36
|
-
## 🚀
|
|
38
|
+
## 🚀 Quick Start
|
|
39
|
+
|
|
40
|
+
Get up and running in seconds:
|
|
37
41
|
|
|
38
42
|
```ts
|
|
39
|
-
|
|
40
|
-
import { createLogger, format, transports } from 'scribelog';
|
|
43
|
+
import { createLogger } from 'scribelog';
|
|
41
44
|
|
|
42
|
-
//
|
|
45
|
+
// Logger with default settings:
|
|
43
46
|
// - Level: 'info'
|
|
44
|
-
// - Format: Simple, colored output
|
|
47
|
+
// - Format: Simple, colored output (if TTY)
|
|
45
48
|
// - Transport: Console
|
|
46
49
|
const logger = createLogger();
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
logger.
|
|
50
|
-
logger.warn('Warning: Cache memory usage high.', { usage: '85%' }); // Add metadata
|
|
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 });
|
|
59
|
-
|
|
60
|
-
logger.info('Operation completed', { user: 'admin', durationMs: 120 });
|
|
61
|
-
|
|
62
|
-
// Debug logs won't appear with the default 'info' level
|
|
63
|
-
logger.debug('Detailed step for debugging.');
|
|
64
|
-
|
|
65
|
-
// --- Example with JSON format and debug level ---
|
|
66
|
-
const jsonLogger = createLogger({
|
|
67
|
-
level: 'debug', // Log 'debug' and higher levels
|
|
68
|
-
format: format.defaultJsonFormat, // Use predefined JSON format (includes errors, timestamp, etc.)
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
jsonLogger.debug('Debugging operation X', { operationId: 'op-xyz' });
|
|
72
|
-
// Example JSON Output:
|
|
73
|
-
// {"level":"debug","message":"Debugging operation X","timestamp":"...ISO_STRING...","operationId":"op-xyz"}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Example Output (Default Simple Format with Colors):**
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
# (Timestamp will be gray, [INFO] green, [WARN] yellow, [ERROR] red)
|
|
80
|
-
2024-05-01T10:00:00.123Z [INFO]: Application started successfully.
|
|
81
|
-
2024-05-01T10:00:01.456Z [WARN]: Warning: Cache memory usage high. { usage: '85%' }
|
|
82
|
-
2024-05-01T10:00:02.789Z [ERROR]: Database connection timeout { exception: true, eventType: undefined, errorName: 'Error', code: 'DB_TIMEOUT' }
|
|
83
|
-
Error: Database connection timeout
|
|
84
|
-
at <anonymous>:10:17
|
|
85
|
-
... (stack trace) ...
|
|
86
|
-
2024-05-01T10:00:03.111Z [INFO]: Operation completed { user: 'admin', durationMs: 120 }
|
|
87
|
-
```
|
|
88
|
-
*(Note: `eventType` is undefined here because the error wasn't logged via `handleExceptions`/`handleRejections`)*
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## ⚙️ Configuration
|
|
93
|
-
|
|
94
|
-
Create and configure your logger using `createLogger(options?: LoggerOptions)`.
|
|
95
|
-
|
|
96
|
-
**`LoggerOptions` Interface:**
|
|
97
|
-
|
|
98
|
-
| Option | Type | Default | Description |
|
|
99
|
-
| :------------------ | :--------------------------- | :------------------------- | :-------------------------------------------------------------------------- |
|
|
100
|
-
| `level` | `string` | `'info'` | Minimum level to log (e.g., 'debug', 'warn'). |
|
|
101
|
-
| `format` | `LogFormat` | `format.defaultSimpleFormat` | Default formatter pipeline for the logger. |
|
|
102
|
-
| `transports` | `Transport[]` | `[new transports.Console()]` | Array of transport instances (where to send logs). |
|
|
103
|
-
| `defaultMeta` | `Record<string, any>` | `undefined` | Metadata automatically included in all logs from this instance. |
|
|
104
|
-
| `handleExceptions` | `boolean` | `false` | Catch and log `uncaughtException` events. |
|
|
105
|
-
| `handleRejections` | `boolean` | `false` | Catch and log `unhandledRejection` events. |
|
|
106
|
-
| `exitOnError` | `boolean` | `true` | If handling exceptions/rejections, exit process (`process.exit(1)`) after logging. |
|
|
51
|
+
logger.info('Scribelog is ready!');
|
|
52
|
+
logger.warn('Something seems off...', { detail: 'Cache size exceeded limit' });
|
|
107
53
|
|
|
108
|
-
|
|
54
|
+
const userId = 'user-42';
|
|
55
|
+
const action = 'login';
|
|
56
|
+
logger.info('User %s performed action: %s', userId, action); // Printf-style
|
|
109
57
|
|
|
110
|
-
```ts
|
|
111
|
-
import { createLogger, format, transports } from 'scribelog';
|
|
112
|
-
|
|
113
|
-
const prodLogger = createLogger({
|
|
114
|
-
level: process.env.LOG_LEVEL || 'info', // Read level from environment or default to info
|
|
115
|
-
format: format.defaultJsonFormat, // Use predefined JSON format
|
|
116
|
-
transports: [
|
|
117
|
-
new transports.Console({
|
|
118
|
-
// Console specific options if needed
|
|
119
|
-
}),
|
|
120
|
-
// Future: new transports.File({ filename: '/var/log/app.log', level: 'warn' })
|
|
121
|
-
],
|
|
122
|
-
defaultMeta: {
|
|
123
|
-
service: 'my-prod-service',
|
|
124
|
-
pid: process.pid,
|
|
125
|
-
// You can add more static metadata here
|
|
126
|
-
},
|
|
127
|
-
handleExceptions: true, // Recommended for production
|
|
128
|
-
handleRejections: true, // Recommended for production
|
|
129
|
-
// exitOnError: true // Default, recommended for production
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
prodLogger.info('Production logger initialized.');
|
|
133
58
|
try {
|
|
134
|
-
// Simulate an
|
|
135
|
-
throw new Error('
|
|
136
|
-
} catch
|
|
137
|
-
// Log the
|
|
138
|
-
|
|
59
|
+
// Simulate an error
|
|
60
|
+
throw new Error('Failed to retrieve data');
|
|
61
|
+
} catch(error) {
|
|
62
|
+
// Log the error object correctly
|
|
63
|
+
logger.error('Data retrieval failed', { error: error as Error });
|
|
139
64
|
}
|
|
140
|
-
|
|
141
|
-
// Example of an unhandled rejection that would be caught if not caught here
|
|
142
|
-
// Promise.reject('Something failed asynchronously');
|
|
143
65
|
```
|
|
144
66
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
## 📊 Logging Levels
|
|
67
|
+
**Default Console Output (example):**
|
|
148
68
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
69
|
+
```bash
|
|
70
|
+
2024-05-01T12:00:00.123Z [INFO]: Scribelog is ready!
|
|
71
|
+
2024-05-01T12:00:00.125Z [WARN]: Something seems off... { detail: 'Cache size exceeded limit' }
|
|
72
|
+
2024-05-01T12:00:00.127Z [INFO]: User user-42 performed action: login
|
|
73
|
+
2024-05-01T12:00:00.129Z [ERROR]: Failed to retrieve data { errorName: 'Error', exception: true }
|
|
74
|
+
Error: Failed to retrieve data
|
|
75
|
+
at <anonymous>:... (stack trace)
|
|
153
76
|
```
|
|
154
77
|
|
|
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
78
|
---
|
|
158
79
|
|
|
159
|
-
##
|
|
160
|
-
|
|
161
|
-
Formatters transform the log `info` object before it reaches transports. Use `format.combine(...)` to chain them.
|
|
162
|
-
|
|
163
|
-
**How it Works:**
|
|
164
|
-
`createLogger` -> `log()`/`logEntry()` -> Creates `LogInfo` object -> Passes to `format` function -> `format` function applies its chain (`combine`) -> Result (string or object) passed to `transport.log()`.
|
|
80
|
+
## 📘 Full Documentation
|
|
165
81
|
|
|
166
|
-
|
|
82
|
+
This README covers the basics. For a comprehensive guide covering **all configuration options, formatters (like `json`, custom `timestamp` formats), transports (`FileTransport` with rotation), child loggers, error handling details, and advanced examples**, please see the:
|
|
167
83
|
|
|
168
|
-
|
|
169
|
-
* `alias?: string`: Key for the formatted timestamp (default: `'timestamp'`).
|
|
170
|
-
* `format?: string | ((date: Date) => string)`: `date-fns` format string or custom function (default: ISO 8601).
|
|
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
|
-
```
|
|
175
|
-
* **`format.level(options?)`**: Adds the log level string.
|
|
176
|
-
* `alias?: string`: Key for the level (default: `'level'`).
|
|
177
|
-
* **`format.message(options?)`**: Adds the log message string.
|
|
178
|
-
* `alias?: string`: Key for the message (default: `'message'`).
|
|
179
|
-
* **`format.errors(options?)`**: Extracts info from an `Error` object (expected at `info.error`). Adds `errorName`, `stack?`, `originalReason?` and potentially other error properties to the `info` object. Sets `info.message` to `error.message` if `info.message` was empty. Removes the original `info.error` field. **Place this early in your `combine` chain.**
|
|
180
|
-
* `stack?: boolean`: Include stack trace (default: `true`).
|
|
181
|
-
* **`format.metadata(options?)`**: Gathers all remaining properties into the main object or under an alias. Excludes standard fields added by other formatters (`level`, `message`, `timestamp`, `errorName`, `stack`, `exception`, `eventType` etc.).
|
|
182
|
-
* `alias?: string`: If provided, nest metadata under this key and remove original keys.
|
|
183
|
-
* `exclude?: string[]`: Array of additional keys to exclude from metadata collection.
|
|
184
|
-
* **`format.json(options?)`**: **Terminal Formatter.** Serializes the final `info` object to a JSON string.
|
|
185
|
-
* `space?: string | number`: Pretty-printing spaces for `JSON.stringify`.
|
|
186
|
-
* **`format.simple(options?)`**: **Terminal Formatter.** Creates a human-readable, colored (if TTY) string. Includes `timestamp`, `level`, `message`, `{ metadata }`, and `stack` (on a new line if present).
|
|
187
|
-
* `colors?: boolean`: Force colors on or off (default: auto-detect based on TTY).
|
|
188
|
-
|
|
189
|
-
### Combining Formatters (`format.combine`)
|
|
190
|
-
|
|
191
|
-
The order matters! Formatters run sequentially. Terminal formatters (`json`, `simple`) should be last.
|
|
192
|
-
|
|
193
|
-
```ts
|
|
194
|
-
import { createLogger, format } from 'scribelog';
|
|
195
|
-
|
|
196
|
-
// Example: Log only level, message, and custom timestamp in simple format
|
|
197
|
-
const minimalFormat = format.combine(
|
|
198
|
-
// Note: errors() is not included here
|
|
199
|
-
format.timestamp({ format: 'HH:mm:ss.SSS' }),
|
|
200
|
-
format.level(),
|
|
201
|
-
format.message(),
|
|
202
|
-
// No metadata() - ignores other fields like { extra: '...' }
|
|
203
|
-
format.simple() // simple() will only use timestamp, level, message
|
|
204
|
-
);
|
|
205
|
-
const minimalLogger = createLogger({ format: minimalFormat });
|
|
206
|
-
minimalLogger.info('Minimal log', { extra: 'this is ignored'});
|
|
207
|
-
// Output: 10:45:00.123 [INFO]: Minimal log
|
|
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}}}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### Predefined Formats
|
|
229
|
-
|
|
230
|
-
* `format.defaultSimpleFormat`: Equivalent to `combine(errors({ stack: true }), timestamp(), level(), message(), metadata(), simple())`. **This is the default format for `createLogger`.**
|
|
231
|
-
* `format.defaultJsonFormat`: Equivalent to `combine(errors({ stack: true }), timestamp(), level(), message(), metadata(), json())`.
|
|
84
|
+
➡️ **[Detailed Documentation](./DOCUMENTATION.md)** ⬅️
|
|
232
85
|
|
|
233
86
|
---
|
|
234
87
|
|
|
235
|
-
##
|
|
236
|
-
|
|
237
|
-
Define log destinations. You can use multiple transports.
|
|
88
|
+
## ⚙️ Basic Configuration (Overview)
|
|
238
89
|
|
|
239
|
-
|
|
90
|
+
Configure your logger via `createLogger(options)`. Key options:
|
|
240
91
|
|
|
241
|
-
|
|
92
|
+
* `level`: `'info'` (default), `'debug'`, `'warn'`, etc.
|
|
93
|
+
* `format`: Use `format.combine(...)` with formatters like `format.simple()`, `format.json()`, `format.timestamp()`, `format.splat()`, `format.errors()`, `format.metadata()`. Default is `format.defaultSimpleFormat`.
|
|
94
|
+
* `transports`: Array of `new transports.Console({...})` or `new transports.File({...})`. Default is one Console transport.
|
|
95
|
+
* `defaultMeta`: An object with data to add to every log message.
|
|
96
|
+
* `handleExceptions`, `handleRejections`, `exitOnError`: For automatic error catching.
|
|
242
97
|
|
|
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']`).
|
|
246
|
-
|
|
247
|
-
**Example: Separate Info and Error Streams**
|
|
98
|
+
**Example: Logging JSON to a File**
|
|
248
99
|
|
|
249
100
|
```ts
|
|
250
101
|
import { createLogger, format, transports } from 'scribelog';
|
|
251
102
|
|
|
252
|
-
const
|
|
253
|
-
level: '
|
|
103
|
+
const fileJsonLogger = createLogger({
|
|
104
|
+
level: 'debug',
|
|
105
|
+
// Use the predefined JSON format (includes error handling, splat, timestamp etc.)
|
|
106
|
+
format: format.defaultJsonFormat,
|
|
254
107
|
transports: [
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
level: '
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}),
|
|
261
|
-
// Log only ERRORs to stderr using JSON format
|
|
262
|
-
new transports.Console({
|
|
263
|
-
level: 'error', // Only logs error passed from logger
|
|
264
|
-
format: format.json(), // Use JSON for errors
|
|
265
|
-
useStdErrLevels: ['error'] // Ensure errors go to stderr
|
|
108
|
+
new transports.File({
|
|
109
|
+
filename: 'application.log', // Log to application.log
|
|
110
|
+
level: 'debug', // Log debug and above to the file
|
|
111
|
+
size: '10M', // Rotate at 10 MB
|
|
112
|
+
maxFiles: 5 // Keep 5 rotated files
|
|
266
113
|
})
|
|
267
|
-
]
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
logger.info('User logged in'); // Filtered out by the first transport's level ('warn')
|
|
271
|
-
logger.warn('Disk space low'); // Goes to first console (stdout, simple)
|
|
272
|
-
logger.error('DB Error', { error: new Error('Connection failed')}); // Goes to BOTH (stdout simple, stderr JSON)
|
|
273
|
-
logger.debug('Should not appear'); // Filtered out by logger's level ('info')
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
---
|
|
277
|
-
|
|
278
|
-
## 🌱 Child Loggers
|
|
279
|
-
|
|
280
|
-
Create contextual loggers using `logger.child(defaultMeta)`. They inherit settings but automatically add the specified metadata.
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
import { createLogger } from 'scribelog';
|
|
284
|
-
|
|
285
|
-
const baseLogger = createLogger({ level: 'debug', defaultMeta: { app: 'my-api' } });
|
|
286
|
-
|
|
287
|
-
function processUserData(userId: string) {
|
|
288
|
-
// Create a logger specific to this user's context
|
|
289
|
-
const userLogger = baseLogger.child({ userId, module: 'userProcessing' });
|
|
290
|
-
|
|
291
|
-
userLogger.debug('Starting data processing'); // Includes { app: 'my-api', userId: '...', module: '...' }
|
|
292
|
-
// ...
|
|
293
|
-
userLogger.info('Data processed');
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function processAdminTask(adminId: string) {
|
|
297
|
-
// Create a logger for admin tasks
|
|
298
|
-
const adminLogger = baseLogger.child({ adminId, scope: 'admin' });
|
|
299
|
-
adminLogger.info('Performing admin task');
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
processUserData('user-77');
|
|
303
|
-
processAdminTask('admin-01');
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
## 🛠️ Error Handling
|
|
309
|
-
|
|
310
|
-
Set `handleExceptions: true` and/or `handleRejections: true` in `createLogger` options to automatically log fatal errors.
|
|
311
|
-
|
|
312
|
-
```ts
|
|
313
|
-
import { createLogger, format } from 'scribelog';
|
|
314
|
-
|
|
315
|
-
const logger = createLogger({
|
|
316
|
-
level: 'info',
|
|
317
|
-
format: format.defaultJsonFormat, // Log errors as JSON for easier parsing
|
|
318
|
-
handleExceptions: true,
|
|
319
|
-
handleRejections: true,
|
|
320
|
-
exitOnError: true // Default behavior: Exit after logging fatal error
|
|
114
|
+
],
|
|
115
|
+
defaultMeta: { service: 'file-writer' }
|
|
321
116
|
});
|
|
322
117
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// Example of what would be caught:
|
|
326
|
-
// setTimeout(() => { throw new Error('Something broke badly!'); }, 50);
|
|
327
|
-
// Output (JSON): {"level":"error","message":"Something broke badly!","timestamp":"...","exception":true,"eventType":"uncaughtException","errorName":"Error","stack":"..."}
|
|
328
|
-
// ... and process exits
|
|
329
|
-
|
|
330
|
-
// Example of what would be caught:
|
|
331
|
-
// Promise.reject('Unhandled promise rejection reason');
|
|
332
|
-
// Output (JSON): {"level":"error","message":"Unhandled promise rejection reason","timestamp":"...","exception":true,"eventType":"unhandledRejection","errorName":"Error","stack":"...","originalReason":"..."}
|
|
333
|
-
// ... and process exits
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
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
|
-
|
|
338
|
-
## 💻 Showcase
|
|
339
|
-
|
|
340
|
-
You can run this script to see how scribelog works.
|
|
341
|
-
|
|
342
|
-
<details>
|
|
343
|
-
<summary>Click to show/hide showcase.ts code</summary>
|
|
344
|
-
|
|
345
|
-
```ts
|
|
346
|
-
// showcase.ts
|
|
347
|
-
// Import everything needed from scribelog
|
|
348
|
-
import {
|
|
349
|
-
createLogger,
|
|
350
|
-
format, // Object containing formatters
|
|
351
|
-
transports, // Object containing transports
|
|
352
|
-
Logger, // Logger interface type
|
|
353
|
-
LogLevel, // Log level type ('info', 'debug', etc.)
|
|
354
|
-
LogFormat, // Formatter function type
|
|
355
|
-
Transport, // Transport interface type
|
|
356
|
-
LoggerOptions // Configuration options type
|
|
357
|
-
} from 'scribelog'; // Import from your published package
|
|
358
|
-
// Import 'process' for PID access
|
|
359
|
-
import process from 'process';
|
|
360
|
-
// Import 'crypto' for generating unique IDs
|
|
361
|
-
import crypto from 'crypto';
|
|
362
|
-
// Import 'date-fns' for custom date formatting (used in one example)
|
|
363
|
-
import { format as formatDate } from 'date-fns';
|
|
364
|
-
// Import chalk to demonstrate color control (optional for user)
|
|
365
|
-
import chalk from 'chalk';
|
|
366
|
-
|
|
367
|
-
// --- MAIN DEMO FUNCTION ---
|
|
368
|
-
async function runShowcase() {
|
|
369
|
-
console.log('\n===========================================');
|
|
370
|
-
console.log('🚀 Scribelog Showcase - All Features Demo 🚀');
|
|
371
|
-
console.log('===========================================\n');
|
|
372
|
-
|
|
373
|
-
// === 1. Basic Configuration & Levels ===
|
|
374
|
-
console.log('--- 1. Basic Configuration & Levels ---');
|
|
375
|
-
// Create a logger with a specific level. Default format is 'simple'.
|
|
376
|
-
const logger1 = createLogger({ level: 'debug' }); // Set level to 'debug' to see more logs
|
|
377
|
-
logger1.info('Logger 1 (level: debug, format: simple)');
|
|
378
|
-
logger1.warn('Warning from Logger 1');
|
|
379
|
-
logger1.debug('Debug message from Logger 1 (should appear)');
|
|
380
|
-
logger1.error('Error from Logger 1'); // Default ConsoleTransport sends this to stderr
|
|
381
|
-
|
|
382
|
-
// === 2. Different Formats ===
|
|
383
|
-
console.log('\n--- 2. Different Formats ---');
|
|
384
|
-
|
|
385
|
-
// 2a. JSON Format
|
|
386
|
-
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
|
-
});
|
|
391
|
-
logger2a.info('Log in JSON format.', { data: true, value: 123 });
|
|
392
|
-
const jsonError = new Error("JSON Formatted Error");
|
|
393
|
-
(jsonError as any).code = "E_JSON"; // Add custom error property
|
|
394
|
-
// Log an error object within metadata
|
|
395
|
-
logger2a.error('An error occurred (JSON)', { error: jsonError, user: 'admin' });
|
|
396
|
-
|
|
397
|
-
// 2b. Custom Simple Format (Colored)
|
|
398
|
-
console.log('\n--- 2b. Custom Simple Format (Colored) ---');
|
|
399
|
-
const customSimpleFormat = format.combine(
|
|
400
|
-
format.timestamp({ format: 'HH:mm:ss' }), // Custom time format only
|
|
401
|
-
format.level(), // Add level string
|
|
402
|
-
format.message(), // Add message string
|
|
403
|
-
format.metadata({ exclude: ['pid'] }), // Add other metadata, but exclude 'pid'
|
|
404
|
-
format.errors({ stack: false }), // Add error info (name, message) but no stack
|
|
405
|
-
format.simple({ colors: true }) // Force colors ON for the output string
|
|
406
|
-
);
|
|
407
|
-
const logger2b = createLogger({ format: customSimpleFormat, level: 'info' });
|
|
408
|
-
const originalChalkLevel2b = chalk.level; // Store current chalk level
|
|
409
|
-
chalk.level = 1; // Force basic colors for this demo section
|
|
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 original chalk level
|
|
413
|
-
|
|
414
|
-
// 2c. Custom JSON Format with Aliases and Nesting
|
|
415
|
-
console.log('\n--- 2c. Custom JSON Format (Aliases & Nesting) ---');
|
|
416
|
-
const customJson = format.combine(
|
|
417
|
-
format.timestamp({ alias: '@timestamp' }), // Rename timestamp field
|
|
418
|
-
format.level({ alias: 'severity' }), // Rename level field
|
|
419
|
-
format.message(), // Keep message field
|
|
420
|
-
format.errors({ stack: true }), // Include full error details + stack
|
|
421
|
-
format.metadata({ alias: 'details' }), // Nest all other metadata under 'details'
|
|
422
|
-
format.json() // Output as JSON
|
|
423
|
-
);
|
|
424
|
-
const logger2c = createLogger({ format: customJson, level: 'info' });
|
|
425
|
-
logger2c.warn('Custom JSON with nested meta', { transactionId: 'xyz', status: 'WARN' });
|
|
426
|
-
const nestedError = new Error("Nested Error");
|
|
427
|
-
nestedError.stack = "Fake stack\n at place";
|
|
428
|
-
logger2c.error("Error with nested meta", { error: nestedError, code: 503 });
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
// === 3. Multiple Transports ===
|
|
432
|
-
console.log('\n--- 3. Multiple Transports ---');
|
|
433
|
-
const originalChalkLevel3 = chalk.level; // Store current chalk level
|
|
434
|
-
chalk.level = 0; // Disable colors globally for easier comparison of output
|
|
435
|
-
const logger3 = createLogger({
|
|
436
|
-
level: 'debug', // Main logger allows all levels through
|
|
437
|
-
transports: [
|
|
438
|
-
// Transport 1: Logs 'info' and above, uses simple format, errors to stderr
|
|
439
|
-
new transports.Console({
|
|
440
|
-
level: 'info', // Transport-specific level filter
|
|
441
|
-
format: format.defaultSimpleFormat, // Use simple format (colors off due to global chalk level)
|
|
442
|
-
useStdErrLevels: ['error'] // Only 'error' level goes to stderr
|
|
443
|
-
}),
|
|
444
|
-
// Transport 2: Logs 'debug' and above, uses JSON format, warn/error to stderr
|
|
445
|
-
new transports.Console({
|
|
446
|
-
level: 'debug', // Transport-specific level filter
|
|
447
|
-
format: format.defaultJsonFormat, // Use JSON format
|
|
448
|
-
useStdErrLevels: ['warn', 'error'] // 'warn' and 'error' go to stderr
|
|
449
|
-
})
|
|
450
|
-
]
|
|
451
|
-
});
|
|
452
|
-
logger3.debug('Debug log (JSON only)'); // Only Transport 2 logs this
|
|
453
|
-
logger3.info('Info log (Simple on stdout & JSON on stdout)'); // Both transports log (both to stdout)
|
|
454
|
-
logger3.warn('Warn log (Simple on stdout & JSON on stderr)'); // Both transports log (JSON to stderr)
|
|
455
|
-
logger3.error('Error log (Simple on stderr & JSON on stderr)'); // Both transports log (both to stderr)
|
|
456
|
-
chalk.level = originalChalkLevel3; // Restore original chalk level
|
|
457
|
-
|
|
458
|
-
// === 4. Child Loggers ===
|
|
459
|
-
console.log('\n--- 4. Child Loggers ---');
|
|
460
|
-
// Parent logger setup
|
|
461
|
-
const parentLogger = createLogger({
|
|
462
|
-
level: 'debug', // Set parent level
|
|
463
|
-
format: format.simple({ colors: false }), // Use simple format without colors for clarity
|
|
464
|
-
defaultMeta: { service: 'MainService' } // Parent's default metadata
|
|
465
|
-
});
|
|
466
|
-
parentLogger.info('Parent log.'); // Contains { service: 'MainService' }
|
|
467
|
-
|
|
468
|
-
// Create first child, inheriting settings but adding 'module'
|
|
469
|
-
const childLogger1 = parentLogger.child({ module: 'ModuleA' });
|
|
470
|
-
childLogger1.info('Child 1 log.'); // Contains { service: 'MainService', module: 'ModuleA' }
|
|
471
|
-
|
|
472
|
-
// Create a child of the first child, inheriting and overriding 'module'
|
|
473
|
-
const childLogger2 = childLogger1.child({ function: 'doWork', module: 'OverrideModule' });
|
|
474
|
-
childLogger2.debug('Child 2 log (debug).', { value: 42 }); // Contains { service: 'MainService', module: 'OverrideModule', function: 'doWork', value: 42 }
|
|
475
|
-
|
|
476
|
-
// === 5. logEntry Method ===
|
|
477
|
-
console.log('\n--- 5. logEntry Method ---');
|
|
478
|
-
// Create a logger specifically for HTTP level logs
|
|
479
|
-
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
|
-
entryLogger.logEntry({
|
|
483
|
-
level: 'http', // Must be >= logger's level ('http')
|
|
484
|
-
message: 'Manual log entry with custom data',
|
|
485
|
-
// Add any custom fields relevant to this log
|
|
486
|
-
method: 'POST',
|
|
487
|
-
url: '/api/users',
|
|
488
|
-
statusCode: 201,
|
|
489
|
-
});
|
|
490
|
-
// Another example
|
|
491
|
-
entryLogger.logEntry({ level: 'info', message: 'This info entry will also show' }); // info > http
|
|
492
|
-
|
|
493
|
-
// === 6. Exception/Rejection Handling (Simulation) ===
|
|
494
|
-
console.log('\n--- 6. Exception/Rejection Handling (Simulating real events) ---');
|
|
495
|
-
// IMPORTANT: Set this to true ONLY if you want the script to exit upon error.
|
|
496
|
-
// Set to false for this demo to see both handlers potentially log.
|
|
497
|
-
const exitOnHandler = false;
|
|
498
|
-
let errorHandlingLogger: Logger | undefined = undefined;
|
|
499
|
-
|
|
500
|
-
console.log(`Creating logger with handleExceptions/Rejections, exitOnError: ${exitOnHandler}`);
|
|
501
|
-
errorHandlingLogger = createLogger({
|
|
502
|
-
level: 'debug', // Log everything from the handlers
|
|
503
|
-
transports: [new transports.Console({ format: format.defaultSimpleFormat })], // Use simple format for errors
|
|
504
|
-
handleExceptions: true, // Enable uncaughtException handler
|
|
505
|
-
handleRejections: true, // Enable unhandledRejection handler
|
|
506
|
-
exitOnError: exitOnHandler // Control whether process exits
|
|
507
|
-
});
|
|
508
|
-
errorHandlingLogger.info(`Error handlers active (exitOnError: ${exitOnHandler}).`);
|
|
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.
|
|
513
|
-
|
|
514
|
-
console.log("Simulating unhandled rejection (will be logged)...");
|
|
515
|
-
// Intentionally create an unhandled rejection
|
|
516
|
-
Promise.reject("Simulated rejection reason (handled by logger)");
|
|
517
|
-
|
|
518
|
-
console.log("Simulating uncaught exception in 100ms (will be logged)...");
|
|
519
|
-
const exceptionTimer = setTimeout(() => {
|
|
520
|
-
// Simulate an error that wasn't caught by application code
|
|
521
|
-
throw new Error("Simulated uncaught exception (handled by logger)");
|
|
522
|
-
// Note: If exitOnError were true, the process would exit shortly after logging this.
|
|
523
|
-
}, 100);
|
|
524
|
-
|
|
525
|
-
// --- Wait and Cleanup (only necessary if exitOnError is false) ---
|
|
526
|
-
if (!exitOnHandler) {
|
|
527
|
-
console.log("Waiting briefly for handlers to potentially run...");
|
|
528
|
-
await new Promise(resolve => setTimeout(resolve, 300)); // Wait longer than the exception timeout
|
|
529
|
-
|
|
530
|
-
console.log("Attempting to remove handlers (as exitOnError was false)...");
|
|
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
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
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
|
-
} // End runShowcase
|
|
545
|
-
|
|
546
|
-
// Run the main demo function
|
|
547
|
-
runShowcase().catch(e => {
|
|
548
|
-
// This catch is unlikely to be hit if handleExceptions is true,
|
|
549
|
-
// but good practice to have.
|
|
550
|
-
console.error("!!! Unexpected error running showcase:", e);
|
|
551
|
-
});
|
|
118
|
+
fileJsonLogger.debug('Writing JSON log to file', { id: 1 });
|
|
119
|
+
fileJsonLogger.error('File write error occurred', { error: new Error('Disk full'), file: 'data.txt'});
|
|
552
120
|
```
|
|
553
121
|
|
|
554
|
-
</details>
|
|
555
|
-
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
|
-
theonlytolon
|
|
557
|
-
|
|
558
122
|
---
|
|
559
123
|
|
|
560
124
|
## 📚 Future Work
|
|
561
125
|
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
* **Async Handling:** Better guarantees for transports finishing writes before `exitOnError`.
|
|
126
|
+
* More built-in formatters (e.g., customizable color themes).
|
|
127
|
+
* Ability to define custom logging levels.
|
|
128
|
+
* Improved handling of asynchronous operations in transports (especially for `exitOnError`).
|
|
566
129
|
|
|
567
130
|
---
|
|
568
131
|
|
|
569
132
|
## 🤝 Contributing
|
|
570
133
|
|
|
571
|
-
Contributions are welcome! Please feel free to submit issues and pull requests
|
|
134
|
+
Contributions are welcome! Please feel free to submit issues and pull requests on the [GitHub repository](https://github.com/tolongames/scribelog).
|
|
572
135
|
|
|
573
136
|
---
|
|
574
137
|
|
|
575
138
|
## 📄 License
|
|
576
139
|
|
|
577
140
|
MIT License
|
|
578
|
-
Copyright (c)
|
|
141
|
+
Copyright (c) 2024 tolongames
|
|
579
142
|
See [LICENSE](./LICENSE) for details.
|
package/dist/format.d.ts
CHANGED
|
@@ -14,6 +14,12 @@ interface ErrorsOptions {
|
|
|
14
14
|
stack?: boolean;
|
|
15
15
|
}
|
|
16
16
|
export declare const errors: (options?: ErrorsOptions) => LogFormat;
|
|
17
|
+
/**
|
|
18
|
+
* Formater: Interpoluje wiadomość w stylu printf używając argumentów z `info.splat`.
|
|
19
|
+
* Zastępuje oryginalne pole `info.message`, jeśli interpolacja jest możliwa.
|
|
20
|
+
* Powinien być umieszczony w `combine` PRZED `format.message()` i PRZED `format.metadata()`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const splat: () => LogFormat;
|
|
17
23
|
export declare const metadata: (options?: {
|
|
18
24
|
alias?: string;
|
|
19
25
|
exclude?: string[];
|