0
# Log Writer System
1
2
Abstract writer system for creating custom log output handlers. Writers subscribe to log events and handle actual output formatting and destination routing, enabling pluggable log output across different environments and platforms.
3
4
## Capabilities
5
6
### Abstract Writer Constructor
7
8
Base class for creating custom log writers that handle log output.
9
10
```javascript { .api }
11
/**
12
* Creates a log writer instance
13
* @param {object} env - Environment variables object
14
* @param {object} [options] - Optional configuration
15
* @param {string} [options.defaultNamespace] - Default namespace for logs
16
* @throws {Error} If called without 'new' keyword
17
*/
18
function LogWriter(env, options);
19
```
20
21
**Usage Examples:**
22
23
```javascript
24
const LogWriter = require("log/lib/abstract-writer");
25
26
// Basic writer setup
27
const writer = new LogWriter(process.env);
28
29
// Writer with options
30
const writer = new LogWriter(process.env, {
31
defaultNamespace: "myapp"
32
});
33
34
// Environment variables used:
35
// - LOG_LEVEL: threshold level (error, warning, notice, info, debug)
36
// - LOG_DEBUG: namespace-based debug filter
37
// - DEBUG: fallback debug filter
38
// - LOG_TIME: timestamp format configuration
39
```
40
41
### Static Properties
42
43
Class-level properties and methods for writer configuration.
44
45
```javascript { .api }
46
/**
47
* Maps log levels to display symbols/prefixes
48
* @type {object}
49
*/
50
LogWriter.levelPrefixes;
51
52
/**
53
* Resolves namespace message prefix for display
54
* @param {Logger} logger - Logger instance
55
* @returns {string|null} Namespace prefix or null
56
*/
57
LogWriter.resolveNamespaceMessagePrefix(logger);
58
```
59
60
**Usage Examples:**
61
62
```javascript
63
const LogWriter = require("log/lib/abstract-writer");
64
65
// Level symbols/prefixes
66
console.log(LogWriter.levelPrefixes);
67
// { debug: "●", info: "ℹ", notice: "ℹ", warning: "⚠", error: "✖" }
68
69
// Namespace prefix resolution
70
const log = require("log");
71
const appLogger = log.get("myapp:service");
72
73
const prefix = LogWriter.resolveNamespaceMessagePrefix(appLogger);
74
console.log(prefix); // "myapp:service" or shortened version
75
```
76
77
### Instance Methods
78
79
Methods available on writer instances for handling log events.
80
81
```javascript { .api }
82
/**
83
* Check if a logger is enabled for output
84
* @param {Logger} logger - Logger instance to check
85
* @returns {boolean} True if logger should output
86
*/
87
writer.isLoggerEnabled(logger);
88
89
/**
90
* Set up level-specific properties on logger
91
* @param {Logger} logger - Logger instance to configure
92
*/
93
writer.setupLevelLogger(logger);
94
95
/**
96
* Set up level message prefix on logger
97
* @param {Logger} logger - Logger instance to configure
98
*/
99
writer.setupLevelMessagePrefix(logger);
100
101
/**
102
* Abstract method - must be implemented by subclasses
103
* @param {LogEvent} event - Log event to write
104
*/
105
writer.writeMessage(event);
106
107
/**
108
* Resolve message tokens into formatted strings
109
* @param {LogEvent} event - Log event to process
110
*/
111
writer.resolveMessageTokens(event);
112
113
/**
114
* Resolve final message from tokens
115
* @param {LogEvent} event - Log event to process
116
*/
117
writer.resolveMessage(event);
118
```
119
120
### Master Writer Registration
121
122
Global writer registration system for managing the active log writer.
123
124
```javascript { .api }
125
/**
126
* Get the currently registered master writer
127
* @returns {LogWriter|null} Active writer or null
128
*/
129
function getMasterWriter();
130
131
/**
132
* Register a master log writer (only one allowed)
133
* @param {LogWriter} writer - Writer instance to register
134
* @returns {LogWriter} The registered writer
135
* @throws {Error} If master writer already registered or invalid writer
136
*/
137
getMasterWriter.register(writer);
138
```
139
140
**Usage Examples:**
141
142
```javascript
143
const getMasterWriter = require("log/lib/get-master-writer");
144
145
// Check current master writer
146
const currentWriter = getMasterWriter();
147
console.log(currentWriter); // null if none registered
148
149
// Register a new writer
150
const writer = new MyCustomWriter(process.env);
151
getMasterWriter.register(writer);
152
153
// Attempting to register another throws error
154
try {
155
getMasterWriter.register(anotherWriter);
156
} catch (e) {
157
console.log(e.message); // "Cannot register: Master log writer already registered"
158
}
159
```
160
161
## Event System
162
163
### Log Events
164
165
Writers receive log events through the global event emitter.
166
167
```javascript { .api }
168
/**
169
* Log event structure passed to writers
170
*/
171
interface LogEvent {
172
/** Logger instance that generated the event */
173
logger: Logger;
174
/** Array of message arguments passed to logger */
175
messageTokens: any[];
176
/** Resolved message string (set by writer) */
177
message?: string;
178
}
179
180
/**
181
* Init event structure for new logger initialization
182
*/
183
interface InitEvent {
184
/** Newly initialized logger instance */
185
logger: Logger;
186
}
187
```
188
189
### Event Emitter
190
191
Global event emitter for log system events.
192
193
```javascript { .api }
194
/**
195
* Global log event emitter
196
* Events:
197
* - 'log': Emitted when logger is called with message
198
* - 'init': Emitted when new logger is initialized
199
*/
200
const emitter = require("log/lib/emitter");
201
```
202
203
**Usage Examples:**
204
205
```javascript
206
const emitter = require("log/lib/emitter");
207
208
// Listen for log events
209
emitter.on("log", (event) => {
210
console.log("Log event:", {
211
level: event.logger.level,
212
namespace: event.logger.namespace,
213
tokens: event.messageTokens
214
});
215
});
216
217
// Listen for logger initialization
218
emitter.on("init", (event) => {
219
console.log("New logger:", {
220
level: event.logger.level,
221
namespace: event.logger.namespace
222
});
223
});
224
```
225
226
## Custom Writer Implementation
227
228
### Basic Writer Example
229
230
```javascript
231
const LogWriter = require("log/lib/abstract-writer");
232
233
class ConsoleWriter extends LogWriter {
234
constructor(env, options) {
235
super(env, options);
236
}
237
238
writeMessage(event) {
239
const { logger, message } = event;
240
241
// Get level prefix
242
const levelPrefix = this.constructor.levelPrefixes[logger.level];
243
244
// Get namespace prefix
245
const namespacePrefix = this.constructor.resolveNamespaceMessagePrefix(logger);
246
247
// Format output
248
let output = `${levelPrefix} `;
249
if (namespacePrefix) {
250
output += `[${namespacePrefix}] `;
251
}
252
output += message;
253
254
// Write to console
255
console.log(output);
256
}
257
}
258
259
// Initialize writer
260
new ConsoleWriter(process.env);
261
```
262
263
### Advanced Writer Features
264
265
```javascript
266
class AdvancedWriter extends LogWriter {
267
constructor(env, options) {
268
super(env, options);
269
this.logFile = options?.logFile;
270
}
271
272
writeMessage(event) {
273
const { logger } = event;
274
275
// Custom filtering
276
if (!this.shouldLog(logger)) {
277
return;
278
}
279
280
// Custom formatting
281
const formattedMessage = this.formatMessage(event);
282
283
// Multiple outputs
284
console.log(formattedMessage);
285
286
if (this.logFile) {
287
this.writeToFile(formattedMessage);
288
}
289
}
290
291
shouldLog(logger) {
292
// Custom logic for log filtering
293
return logger.isEnabled && logger.levelIndex <= 2;
294
}
295
296
formatMessage(event) {
297
const timestamp = new Date().toISOString();
298
const { logger, message } = event;
299
300
return `${timestamp} [${logger.level.toUpperCase()}] ${logger.namespace || 'ROOT'}: ${message}`;
301
}
302
303
writeToFile(message) {
304
// File writing logic
305
require('fs').appendFileSync(this.logFile, message + '\n');
306
}
307
}
308
```
309
310
## Environment Configuration
311
312
### Environment Variables
313
314
Writers use environment variables for configuration:
315
316
```javascript
317
// LOG_LEVEL: Sets visibility threshold
318
// Values: error, warning, notice, info, debug
319
// Default: notice
320
process.env.LOG_LEVEL = "info";
321
322
// LOG_DEBUG: Namespace-based debug filtering
323
// Format: comma-separated namespaces, "-" prefix to disable
324
// Examples: "myapp", "service:*", "db,-db:verbose"
325
process.env.LOG_DEBUG = "myapp:service,database";
326
327
// DEBUG: Fallback debug filter (debug-lib compatible)
328
process.env.DEBUG = "myapp:*";
329
330
// LOG_TIME: Timestamp configuration
331
// Implementation-specific format
332
process.env.LOG_TIME = "iso";
333
```
334
335
### Visibility Control
336
337
Writers automatically apply visibility control based on level thresholds and namespace filters:
338
339
```javascript
340
const LogWriter = require("log/lib/abstract-writer");
341
342
// Environment setup
343
process.env.LOG_LEVEL = "notice"; // Show notice and above
344
process.env.LOG_DEBUG = "myapp"; // Also show debug logs for "myapp" namespace
345
346
const writer = new MyWriter(process.env);
347
348
// These will be visible (notice and above):
349
log.error("Error message"); // ✓ Visible (error >= notice)
350
log.warning("Warning"); // ✓ Visible (warning >= notice)
351
log.notice("Notice"); // ✓ Visible (notice >= notice)
352
353
// These will be hidden (below notice threshold):
354
log.info("Info message"); // ✗ Hidden (info < notice)
355
log.debug("Debug message"); // ✗ Hidden (debug < notice)
356
357
// But debug namespace is enabled:
358
log.get("myapp").debug("Debug in myapp"); // ✓ Visible (namespace filter)
359
```