zario 0.3.1 → 0.3.5
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 +161 -1
- package/dist/cjs/aggregation/LogAggregator.js +110 -0
- package/dist/cjs/aggregation/index.js +17 -0
- package/dist/cjs/core/Logger.js +106 -2
- package/dist/cjs/filters/Filter.js +56 -0
- package/dist/cjs/filters/SpecificFilters.js +71 -0
- package/dist/cjs/filters/index.js +18 -0
- package/dist/cjs/index.js +18 -1
- package/dist/cjs/structured/StructuredExtensions.js +112 -0
- package/dist/cjs/structured/index.js +17 -0
- package/dist/cjs/transports/FilterableTransport.js +39 -0
- package/dist/cjs/transports/index.js +1 -0
- package/dist/esm/aggregation/LogAggregator.d.ts +60 -0
- package/dist/esm/aggregation/LogAggregator.js +104 -0
- package/dist/esm/aggregation/index.d.ts +1 -0
- package/dist/esm/aggregation/index.js +1 -0
- package/dist/esm/core/Logger.d.ts +33 -0
- package/dist/esm/core/Logger.js +106 -2
- package/dist/esm/filters/Filter.d.ts +47 -0
- package/dist/esm/filters/Filter.js +49 -0
- package/dist/esm/filters/SpecificFilters.d.ts +41 -0
- package/dist/esm/filters/SpecificFilters.js +64 -0
- package/dist/esm/filters/index.d.ts +2 -0
- package/dist/esm/filters/index.js +2 -0
- package/dist/esm/index.d.ts +6 -3
- package/dist/esm/index.js +11 -2
- package/dist/esm/structured/StructuredExtensions.d.ts +31 -0
- package/dist/esm/structured/StructuredExtensions.js +74 -0
- package/dist/esm/structured/index.d.ts +1 -0
- package/dist/esm/structured/index.js +1 -0
- package/dist/esm/transports/FilterableTransport.d.ts +14 -0
- package/dist/esm/transports/FilterableTransport.js +35 -0
- package/dist/esm/transports/index.d.ts +1 -0
- package/dist/esm/transports/index.js +1 -0
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
A minimal, fast logging library for Node.js with TypeScript support.
|
|
4
4
|
|
|
5
|
+
## What's New in 0.3.5
|
|
6
|
+
|
|
7
|
+
- Added advanced filtering capabilities with LevelFilter, PrefixFilter, MetadataFilter, CompositeFilter, OrFilter, and PredicateFilter
|
|
8
|
+
- Added structured logging extensions with LogEnrichmentPipeline and MetadataEnricher
|
|
9
|
+
- Added TimeBasedAggregator.stop() method for proper timer cleanup
|
|
10
|
+
- Improved error handling for aggregators and filters with try-catch protection
|
|
11
|
+
- Enhanced thread safety with defensive copying of filters during iteration
|
|
12
|
+
- Fixed enricher inheritance in child loggers
|
|
13
|
+
- Added documentation for empty array behavior in CompositeFilter (allows all) and OrFilter (blocks all)
|
|
14
|
+
- Added note about strict equality in MetadataFilter for complex objects
|
|
15
|
+
|
|
5
16
|
## What's New in 0.2.11
|
|
6
17
|
|
|
7
18
|
- Added HTTP transport support with new HttpTransport class
|
|
@@ -46,6 +57,9 @@ logger.error("❌ Database connection failed", { code: 500 });
|
|
|
46
57
|
| **transports** | `array` | Where logs are written (with transport-specific options like `path`, `maxSize`, `maxFiles`, `compression`, `batchInterval`, `compressOldFiles` for file transport) |
|
|
47
58
|
| **customLevels** | `object` | Define custom log levels and their priorities |
|
|
48
59
|
| **customColors** | `object` | Assign colors to custom log levels |
|
|
60
|
+
| **filters** | `Filter[]` | Array of filters to apply before logging (LevelFilter, PrefixFilter, MetadataFilter, CompositeFilter, OrFilter, PredicateFilter) |
|
|
61
|
+
| **aggregators** | `LogAggregator[]` | Array of log aggregators (BatchAggregator, TimeBasedAggregator, CompositeAggregator) |
|
|
62
|
+
| **enrichers** | `LogEnrichmentPipeline` | Pipeline for structured logging extensions |
|
|
49
63
|
|
|
50
64
|
### Log Levels
|
|
51
65
|
|
|
@@ -112,16 +126,162 @@ const logger = new Logger({
|
|
|
112
126
|
});
|
|
113
127
|
```
|
|
114
128
|
|
|
129
|
+
### Advanced Filtering
|
|
130
|
+
|
|
131
|
+
Filter logs based on various criteria using built-in filter classes:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
import {
|
|
135
|
+
Logger,
|
|
136
|
+
ConsoleTransport,
|
|
137
|
+
LevelFilter,
|
|
138
|
+
PrefixFilter,
|
|
139
|
+
MetadataFilter,
|
|
140
|
+
CompositeFilter,
|
|
141
|
+
OrFilter,
|
|
142
|
+
PredicateFilter
|
|
143
|
+
} from "zario";
|
|
144
|
+
|
|
145
|
+
// Level filter - only allow specific log levels
|
|
146
|
+
const levelFilter = new LevelFilter(['info', 'error']);
|
|
147
|
+
|
|
148
|
+
// Prefix filter - only allow logs with specific prefixes
|
|
149
|
+
const prefixFilter = new PrefixFilter(['[API]', '[DB]']);
|
|
150
|
+
|
|
151
|
+
// Metadata filter - only allow logs with specific metadata
|
|
152
|
+
const metadataFilter = new MetadataFilter({ userId: 123 });
|
|
153
|
+
|
|
154
|
+
// Composite filter - combines multiple filters with AND logic
|
|
155
|
+
// Note: With an empty array, CompositeFilter allows all logs (vacuous truth)
|
|
156
|
+
const compositeFilter = new CompositeFilter([levelFilter, prefixFilter]);
|
|
157
|
+
|
|
158
|
+
// Or filter - combines multiple filters with OR logic
|
|
159
|
+
// Note: With an empty array, OrFilter blocks all logs (no matching conditions)
|
|
160
|
+
const orFilter = new OrFilter([levelFilter, metadataFilter]);
|
|
161
|
+
|
|
162
|
+
// Predicate filter - custom filtering function
|
|
163
|
+
const predicateFilter = new PredicateFilter((logData) => {
|
|
164
|
+
return logData.level !== 'debug'; // Filter out debug messages
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Create logger with filters
|
|
168
|
+
const filteredLogger = new Logger({
|
|
169
|
+
level: 'debug',
|
|
170
|
+
transports: [new ConsoleTransport()],
|
|
171
|
+
filters: [compositeFilter, predicateFilter] // Apply multiple filters
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Structured Logging Extensions
|
|
176
|
+
|
|
177
|
+
Enhance your logs with additional metadata using structured logging extensions:
|
|
178
|
+
|
|
179
|
+
```js
|
|
180
|
+
import {
|
|
181
|
+
Logger,
|
|
182
|
+
ConsoleTransport,
|
|
183
|
+
LogEnrichmentPipeline,
|
|
184
|
+
MetadataEnricher
|
|
185
|
+
} from "zario";
|
|
186
|
+
|
|
187
|
+
// Create enrichers to add metadata to logs
|
|
188
|
+
const staticEnricher = MetadataEnricher.addStaticFields({
|
|
189
|
+
service: 'user-service',
|
|
190
|
+
version: '1.0.0'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const dynamicEnricher = MetadataEnricher.addDynamicFields(() => ({
|
|
194
|
+
processId: process.pid,
|
|
195
|
+
memoryUsage: process.memoryUsage().heapUsed
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
const processEnricher = MetadataEnricher.addProcessInfo();
|
|
199
|
+
const envEnricher = MetadataEnricher.addEnvironmentInfo();
|
|
200
|
+
|
|
201
|
+
// Create a pipeline with multiple enrichers
|
|
202
|
+
const enricherPipeline = new LogEnrichmentPipeline([
|
|
203
|
+
staticEnricher,
|
|
204
|
+
dynamicEnricher,
|
|
205
|
+
processEnricher,
|
|
206
|
+
envEnricher
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
// Create logger with enrichers
|
|
210
|
+
const enrichedLogger = new Logger({
|
|
211
|
+
level: 'info',
|
|
212
|
+
transports: [new ConsoleTransport()],
|
|
213
|
+
enrichers: enricherPipeline
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
enrichedLogger.info('User login', { userId: 123 });
|
|
217
|
+
// Output will include additional metadata fields
|
|
218
|
+
|
|
219
|
+
// Add enrichers dynamically
|
|
220
|
+
enrichedLogger.addEnricher((logData) => {
|
|
221
|
+
return {
|
|
222
|
+
...logData,
|
|
223
|
+
timestamp: new Date().toISOString(),
|
|
224
|
+
additionalField: 'some-value'
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Log Aggregation
|
|
230
|
+
|
|
231
|
+
Aggregate logs in batches or based on time intervals:
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
import {
|
|
235
|
+
Logger,
|
|
236
|
+
ConsoleTransport,
|
|
237
|
+
BatchAggregator,
|
|
238
|
+
TimeBasedAggregator,
|
|
239
|
+
CompositeAggregator
|
|
240
|
+
} from "zario";
|
|
241
|
+
|
|
242
|
+
// Batch aggregator - flushes when batch size is reached
|
|
243
|
+
const batchAggregator = new BatchAggregator(10, (logs) => {
|
|
244
|
+
// Process batch of 10 logs
|
|
245
|
+
console.log(`Processing ${logs.length} logs`);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Time-based aggregator - flushes after time interval
|
|
249
|
+
const timeAggregator = new TimeBasedAggregator(5000, (logs) => {
|
|
250
|
+
// Process logs every 5 seconds
|
|
251
|
+
console.log(`Processing ${logs.length} logs`);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Create logger with aggregators
|
|
255
|
+
const aggregatedLogger = new Logger({
|
|
256
|
+
level: 'info',
|
|
257
|
+
transports: [new ConsoleTransport()],
|
|
258
|
+
aggregators: [batchAggregator]
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Manually flush aggregators
|
|
262
|
+
aggregatedLogger.flushAggregators();
|
|
263
|
+
|
|
264
|
+
// Stop time-based aggregator timer
|
|
265
|
+
// timeAggregator.stop(); // Available for TimeBasedAggregator
|
|
266
|
+
```
|
|
267
|
+
|
|
115
268
|
### Methods
|
|
116
269
|
|
|
117
270
|
- `logger.debug(message, metadata?)` - Debug level logging
|
|
118
|
-
- `logger.info(message, metadata?)` - Info level logging
|
|
271
|
+
- `logger.info(message, metadata?)` - Info level logging
|
|
119
272
|
- `logger.warn(message, metadata?)` - Warning level logging
|
|
120
273
|
- `logger.error(message, metadata?)` - Error level logging
|
|
121
274
|
- `logger.logWithLevel(level, message, metadata?)` - Log a message at an arbitrary/custom level
|
|
122
275
|
- `logger.createChild(options)` - Creates a child logger with inherited settings
|
|
123
276
|
- `logger.setLevel(level)` - Change the logger level at runtime
|
|
124
277
|
- `logger.setFormat(format)` - Set the output format to text or json
|
|
278
|
+
- `logger.addFilter(filter)` - Add a filter to the logger (LevelFilter, PrefixFilter, MetadataFilter, etc.)
|
|
279
|
+
- `logger.removeFilter(filter)` - Remove a filter from the logger
|
|
280
|
+
- `logger.addAggregator(aggregator)` - Add an aggregator to the logger (BatchAggregator, TimeBasedAggregator, etc.)
|
|
281
|
+
- `logger.removeAggregator(aggregator)` - Remove an aggregator from the logger
|
|
282
|
+
- `logger.flushAggregators()` - Manually flush all aggregators
|
|
283
|
+
- `logger.addEnricher(enricher)` - Add an enricher to the logger for structured logging
|
|
284
|
+
- `logger.startTimer(name)` - Create a performance timer
|
|
125
285
|
|
|
126
286
|
## Usage Examples
|
|
127
287
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CompositeAggregator = exports.TimeBasedAggregator = exports.BatchAggregator = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Aggregates logs in memory and flushes them in batches
|
|
6
|
+
*/
|
|
7
|
+
class BatchAggregator {
|
|
8
|
+
constructor(maxSize = 100, flushCallback) {
|
|
9
|
+
this.logs = [];
|
|
10
|
+
this.maxSize = maxSize;
|
|
11
|
+
this.flushCallback = flushCallback;
|
|
12
|
+
}
|
|
13
|
+
aggregate(logData, formatter) {
|
|
14
|
+
this.logs.push({ logData, formatter });
|
|
15
|
+
if (this.logs.length >= this.maxSize) {
|
|
16
|
+
const result = this.flush();
|
|
17
|
+
// Handle the case where flush returns a Promise (async flushCallback)
|
|
18
|
+
if (result instanceof Promise) {
|
|
19
|
+
result.catch(error => {
|
|
20
|
+
console.error('Error in BatchAggregator flush callback:', error);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
flush() {
|
|
26
|
+
if (this.logs.length > 0) {
|
|
27
|
+
const logsToFlush = [...this.logs];
|
|
28
|
+
this.logs = [];
|
|
29
|
+
return this.flushCallback(logsToFlush);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.BatchAggregator = BatchAggregator;
|
|
34
|
+
/**
|
|
35
|
+
* Aggregates logs based on a time interval
|
|
36
|
+
*/
|
|
37
|
+
class TimeBasedAggregator {
|
|
38
|
+
constructor(flushInterval, // in milliseconds
|
|
39
|
+
flushCallback) {
|
|
40
|
+
this.logs = [];
|
|
41
|
+
this.timer = null;
|
|
42
|
+
this.flushInterval = flushInterval;
|
|
43
|
+
this.flushCallback = flushCallback;
|
|
44
|
+
}
|
|
45
|
+
aggregate(logData, formatter) {
|
|
46
|
+
this.logs.push({ logData, formatter });
|
|
47
|
+
// Start the timer if it's not already running
|
|
48
|
+
if (!this.timer) {
|
|
49
|
+
this.timer = setTimeout(() => {
|
|
50
|
+
const result = this.flush();
|
|
51
|
+
// Handle the case where flush returns a Promise (async flushCallback)
|
|
52
|
+
if (result instanceof Promise) {
|
|
53
|
+
result.catch(error => {
|
|
54
|
+
console.error('Error in TimeBasedAggregator flush callback:', error);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}, this.flushInterval);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
flush() {
|
|
61
|
+
if (this.logs.length > 0) {
|
|
62
|
+
// Clear the timer if it exists
|
|
63
|
+
if (this.timer) {
|
|
64
|
+
clearTimeout(this.timer);
|
|
65
|
+
this.timer = null;
|
|
66
|
+
}
|
|
67
|
+
const logsToFlush = [...this.logs];
|
|
68
|
+
this.logs = [];
|
|
69
|
+
return this.flushCallback(logsToFlush);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Stop the aggregator and cancel any pending timer without flushing
|
|
74
|
+
*/
|
|
75
|
+
stop() {
|
|
76
|
+
if (this.timer) {
|
|
77
|
+
clearTimeout(this.timer);
|
|
78
|
+
this.timer = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.TimeBasedAggregator = TimeBasedAggregator;
|
|
83
|
+
/**
|
|
84
|
+
* Combines multiple aggregators
|
|
85
|
+
*/
|
|
86
|
+
class CompositeAggregator {
|
|
87
|
+
constructor(aggregators) {
|
|
88
|
+
this.aggregators = aggregators;
|
|
89
|
+
}
|
|
90
|
+
aggregate(logData, formatter) {
|
|
91
|
+
for (const aggregator of this.aggregators) {
|
|
92
|
+
aggregator.aggregate(logData, formatter);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
flush() {
|
|
96
|
+
const results = [];
|
|
97
|
+
for (const aggregator of this.aggregators) {
|
|
98
|
+
const result = aggregator.flush();
|
|
99
|
+
if (result) {
|
|
100
|
+
results.push(result);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If any aggregator returns a promise, wait for all of them
|
|
104
|
+
if (results.some(r => r instanceof Promise)) {
|
|
105
|
+
const promiseResults = results.filter(r => r instanceof Promise);
|
|
106
|
+
return Promise.all(promiseResults).then(() => { });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.CompositeAggregator = CompositeAggregator;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./LogAggregator.js"), exports);
|
package/dist/cjs/core/Logger.js
CHANGED
|
@@ -3,14 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Logger = void 0;
|
|
4
4
|
const Formatter_js_1 = require("./Formatter.js");
|
|
5
5
|
const ConsoleTransport_js_1 = require("../transports/ConsoleTransport.js");
|
|
6
|
+
const StructuredExtensions_js_1 = require("../structured/StructuredExtensions.js");
|
|
6
7
|
class Logger {
|
|
7
8
|
constructor(options = {}) {
|
|
8
9
|
this.transports = [];
|
|
9
|
-
|
|
10
|
+
this.filters = [];
|
|
11
|
+
this.aggregators = [];
|
|
12
|
+
const { level, colorize, json, transports = [], timestampFormat = "YYYY-MM-DD HH:mm:ss", prefix, timestamp, context = {}, parent, asyncMode, customLevels = {}, customColors = {}, filters = [], aggregators = [], enrichers, } = options;
|
|
10
13
|
this.parent = parent; // Set parent
|
|
11
14
|
this.context = { ...context }; // Init context
|
|
12
15
|
this.customLevels = customLevels; // custom log store
|
|
13
16
|
this.asyncMode = false;
|
|
17
|
+
this.filters = [...filters]; // Copy filters
|
|
18
|
+
this.aggregators = [...aggregators]; // Copy aggregators
|
|
19
|
+
this.enrichers = enrichers ?? new StructuredExtensions_js_1.LogEnrichmentPipeline(); // Set enrichers, default to new instance
|
|
14
20
|
if (this.parent) {
|
|
15
21
|
this.level = level ?? this.parent.level;
|
|
16
22
|
this.prefix = prefix ?? this.parent.prefix;
|
|
@@ -36,6 +42,21 @@ class Logger {
|
|
|
36
42
|
this.context = { ...this.parent.context, ...this.context };
|
|
37
43
|
// Merge custom levels with parent's custom levels
|
|
38
44
|
this.customLevels = { ...this.parent.customLevels, ...customLevels };
|
|
45
|
+
// Merge filters with parent's filters
|
|
46
|
+
this.filters = [...this.parent.filters, ...filters];
|
|
47
|
+
// Merge aggregators with parent's aggregators
|
|
48
|
+
this.aggregators = [...this.parent.aggregators, ...aggregators];
|
|
49
|
+
// If child logger doesn't provide its own enrichers, use parent's
|
|
50
|
+
// If child logger provides enrichers, merge parent and child enrichers
|
|
51
|
+
if (enrichers) {
|
|
52
|
+
// Create a new pipeline that combines parent and child enrichers
|
|
53
|
+
const parentEnrichers = this.parent.enrichers.getEnrichers();
|
|
54
|
+
const childEnrichers = enrichers.getEnrichers();
|
|
55
|
+
this.enrichers = new StructuredExtensions_js_1.LogEnrichmentPipeline([...parentEnrichers, ...childEnrichers]);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this.enrichers = this.parent.enrichers;
|
|
59
|
+
}
|
|
39
60
|
}
|
|
40
61
|
else {
|
|
41
62
|
// Auto-configure based on environment
|
|
@@ -140,7 +161,7 @@ class Logger {
|
|
|
140
161
|
finalMetadata = metadata;
|
|
141
162
|
}
|
|
142
163
|
// Only add metadata if it's not empty after merging
|
|
143
|
-
|
|
164
|
+
let logData = {
|
|
144
165
|
level,
|
|
145
166
|
message,
|
|
146
167
|
timestamp,
|
|
@@ -149,6 +170,17 @@ class Logger {
|
|
|
149
170
|
: undefined,
|
|
150
171
|
prefix: this.prefix,
|
|
151
172
|
};
|
|
173
|
+
// Apply enrichers to the log data
|
|
174
|
+
logData = this.enrichers.process(logData);
|
|
175
|
+
// Check if the log should be emitted based on filters
|
|
176
|
+
// Use a copy to prevent concurrent modification issues if filters are modified during logging
|
|
177
|
+
const currentFilters = [...this.filters];
|
|
178
|
+
if (currentFilters.length > 0) {
|
|
179
|
+
const shouldEmit = currentFilters.every(filter => filter.shouldEmit(logData));
|
|
180
|
+
if (!shouldEmit) {
|
|
181
|
+
return; // Don't emit if any filter rejects the log
|
|
182
|
+
}
|
|
183
|
+
}
|
|
152
184
|
if (this.asyncMode) {
|
|
153
185
|
for (const transport of this.transports) {
|
|
154
186
|
if (transport.writeAsync) {
|
|
@@ -168,6 +200,17 @@ class Logger {
|
|
|
168
200
|
transport.write(logData, this.formatter);
|
|
169
201
|
}
|
|
170
202
|
}
|
|
203
|
+
// Send to aggregators if any exist
|
|
204
|
+
if (this.aggregators.length > 0) {
|
|
205
|
+
for (const aggregator of this.aggregators) {
|
|
206
|
+
try {
|
|
207
|
+
aggregator.aggregate(logData, this.formatter);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error('Error in aggregator:', error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
171
214
|
}
|
|
172
215
|
debug(message, metadata) {
|
|
173
216
|
this.log("debug", message, metadata);
|
|
@@ -221,6 +264,67 @@ class Logger {
|
|
|
221
264
|
const { Timer } = require("../utils/Timerutil");
|
|
222
265
|
return new Timer(name, (message) => this.info(message));
|
|
223
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Add a filter to the logger
|
|
269
|
+
*/
|
|
270
|
+
addFilter(filter) {
|
|
271
|
+
this.filters.push(filter);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Remove a filter from the logger
|
|
275
|
+
*/
|
|
276
|
+
removeFilter(filter) {
|
|
277
|
+
const index = this.filters.indexOf(filter);
|
|
278
|
+
if (index !== -1) {
|
|
279
|
+
this.filters.splice(index, 1);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Add an aggregator to the logger
|
|
286
|
+
*/
|
|
287
|
+
addAggregator(aggregator) {
|
|
288
|
+
this.aggregators.push(aggregator);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Remove an aggregator from the logger
|
|
292
|
+
*/
|
|
293
|
+
removeAggregator(aggregator) {
|
|
294
|
+
const index = this.aggregators.indexOf(aggregator);
|
|
295
|
+
if (index !== -1) {
|
|
296
|
+
this.aggregators.splice(index, 1);
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Add an enricher to the logger
|
|
303
|
+
*/
|
|
304
|
+
addEnricher(enricher) {
|
|
305
|
+
this.enrichers.add(enricher);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Flush all aggregators
|
|
309
|
+
*/
|
|
310
|
+
async flushAggregators() {
|
|
311
|
+
const flushPromises = [];
|
|
312
|
+
for (const aggregator of this.aggregators) {
|
|
313
|
+
try {
|
|
314
|
+
const result = aggregator.flush();
|
|
315
|
+
if (result instanceof Promise) {
|
|
316
|
+
// Wrap the promise to catch and log rejections, so one failure doesn't stop others
|
|
317
|
+
flushPromises.push(result.catch(error => {
|
|
318
|
+
console.error('Error flushing aggregator:', error);
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
console.error('Error flushing aggregator:', error);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
await Promise.all(flushPromises);
|
|
327
|
+
}
|
|
224
328
|
}
|
|
225
329
|
exports.Logger = Logger;
|
|
226
330
|
Logger.defaultTransportsFactory = null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PredicateFilter = exports.NotFilter = exports.OrFilter = exports.CompositeFilter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A filter that combines multiple filters with AND logic
|
|
6
|
+
* Note: With an empty array of filters, this returns true (allows all logs).
|
|
7
|
+
* This follows the mathematical concept of vacuous truth - "all" conditions
|
|
8
|
+
* are satisfied when there are no conditions to check.
|
|
9
|
+
*/
|
|
10
|
+
class CompositeFilter {
|
|
11
|
+
constructor(filters) {
|
|
12
|
+
this.filters = filters;
|
|
13
|
+
}
|
|
14
|
+
shouldEmit(logData) {
|
|
15
|
+
return this.filters.every(filter => filter.shouldEmit(logData));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.CompositeFilter = CompositeFilter;
|
|
19
|
+
/**
|
|
20
|
+
* A filter that combines multiple filters with OR logic
|
|
21
|
+
* Note: With an empty array of filters, this returns false (blocks all logs).
|
|
22
|
+
* This is because there are no matching conditions when the filter array is empty.
|
|
23
|
+
*/
|
|
24
|
+
class OrFilter {
|
|
25
|
+
constructor(filters) {
|
|
26
|
+
this.filters = filters;
|
|
27
|
+
}
|
|
28
|
+
shouldEmit(logData) {
|
|
29
|
+
return this.filters.some(filter => filter.shouldEmit(logData));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.OrFilter = OrFilter;
|
|
33
|
+
/**
|
|
34
|
+
* A filter that negates another filter
|
|
35
|
+
*/
|
|
36
|
+
class NotFilter {
|
|
37
|
+
constructor(filter) {
|
|
38
|
+
this.filter = filter;
|
|
39
|
+
}
|
|
40
|
+
shouldEmit(logData) {
|
|
41
|
+
return !this.filter.shouldEmit(logData);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.NotFilter = NotFilter;
|
|
45
|
+
/**
|
|
46
|
+
* A filter based on a predicate function
|
|
47
|
+
*/
|
|
48
|
+
class PredicateFilter {
|
|
49
|
+
constructor(predicate) {
|
|
50
|
+
this.predicate = predicate;
|
|
51
|
+
}
|
|
52
|
+
shouldEmit(logData) {
|
|
53
|
+
return this.predicate(logData);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.PredicateFilter = PredicateFilter;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FieldFilter = exports.MetadataFilter = exports.PrefixFilter = exports.LevelFilter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A filter that allows logs based on their log level
|
|
6
|
+
*/
|
|
7
|
+
class LevelFilter {
|
|
8
|
+
constructor(allowedLevels) {
|
|
9
|
+
this.allowedLevels = new Set(allowedLevels);
|
|
10
|
+
}
|
|
11
|
+
shouldEmit(logData) {
|
|
12
|
+
return this.allowedLevels.has(logData.level);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.LevelFilter = LevelFilter;
|
|
16
|
+
/**
|
|
17
|
+
* A filter that allows logs based on their prefix/namespace
|
|
18
|
+
*/
|
|
19
|
+
class PrefixFilter {
|
|
20
|
+
constructor(allowedPrefixes) {
|
|
21
|
+
this.allowedPrefixes = new Set(allowedPrefixes);
|
|
22
|
+
}
|
|
23
|
+
shouldEmit(logData) {
|
|
24
|
+
if (!logData.prefix) {
|
|
25
|
+
// If no prefix exists, only allow if empty string is in allowed prefixes
|
|
26
|
+
return this.allowedPrefixes.has('');
|
|
27
|
+
}
|
|
28
|
+
return this.allowedPrefixes.has(logData.prefix);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.PrefixFilter = PrefixFilter;
|
|
32
|
+
/**
|
|
33
|
+
* A filter that allows logs based on specific metadata fields
|
|
34
|
+
* Note: Uses strict equality (===) for comparison, which means objects and arrays
|
|
35
|
+
* will only match if they are the same reference. For deep comparison of complex
|
|
36
|
+
* values, consider implementing a custom filter.
|
|
37
|
+
*/
|
|
38
|
+
class MetadataFilter {
|
|
39
|
+
constructor(requiredMetadata) {
|
|
40
|
+
this.requiredMetadata = requiredMetadata;
|
|
41
|
+
}
|
|
42
|
+
shouldEmit(logData) {
|
|
43
|
+
if (!logData.metadata) {
|
|
44
|
+
// If no metadata exists but we require some, return false
|
|
45
|
+
return Object.keys(this.requiredMetadata).length === 0;
|
|
46
|
+
}
|
|
47
|
+
for (const [key, value] of Object.entries(this.requiredMetadata)) {
|
|
48
|
+
if (logData.metadata[key] !== value) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.MetadataFilter = MetadataFilter;
|
|
56
|
+
/**
|
|
57
|
+
* A filter that allows logs based on a custom field in metadata
|
|
58
|
+
*/
|
|
59
|
+
class FieldFilter {
|
|
60
|
+
constructor(fieldName, expectedValue) {
|
|
61
|
+
this.fieldName = fieldName;
|
|
62
|
+
this.expectedValue = expectedValue;
|
|
63
|
+
}
|
|
64
|
+
shouldEmit(logData) {
|
|
65
|
+
if (!logData.metadata) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return logData.metadata[this.fieldName] === this.expectedValue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.FieldFilter = FieldFilter;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Filter.js"), exports);
|
|
18
|
+
__exportStar(require("./SpecificFilters.js"), exports);
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HttpTransport = exports.FileTransport = exports.ConsoleTransport = exports.Logger = void 0;
|
|
3
|
+
exports.LogEnrichmentPipeline = exports.MetadataEnricher = exports.CompositeAggregator = exports.TimeBasedAggregator = exports.BatchAggregator = exports.FieldFilter = exports.MetadataFilter = exports.PrefixFilter = exports.LevelFilter = exports.PredicateFilter = exports.NotFilter = exports.OrFilter = exports.CompositeFilter = exports.FilterableTransport = exports.HttpTransport = exports.FileTransport = exports.ConsoleTransport = exports.Logger = void 0;
|
|
4
4
|
const Logger_js_1 = require("./core/Logger.js");
|
|
5
5
|
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return Logger_js_1.Logger; } });
|
|
6
6
|
const index_js_1 = require("./transports/index.js");
|
|
7
7
|
Object.defineProperty(exports, "ConsoleTransport", { enumerable: true, get: function () { return index_js_1.ConsoleTransport; } });
|
|
8
8
|
Object.defineProperty(exports, "FileTransport", { enumerable: true, get: function () { return index_js_1.FileTransport; } });
|
|
9
9
|
Object.defineProperty(exports, "HttpTransport", { enumerable: true, get: function () { return index_js_1.HttpTransport; } });
|
|
10
|
+
Object.defineProperty(exports, "FilterableTransport", { enumerable: true, get: function () { return index_js_1.FilterableTransport; } });
|
|
11
|
+
const index_js_2 = require("./filters/index.js");
|
|
12
|
+
Object.defineProperty(exports, "CompositeFilter", { enumerable: true, get: function () { return index_js_2.CompositeFilter; } });
|
|
13
|
+
Object.defineProperty(exports, "OrFilter", { enumerable: true, get: function () { return index_js_2.OrFilter; } });
|
|
14
|
+
Object.defineProperty(exports, "NotFilter", { enumerable: true, get: function () { return index_js_2.NotFilter; } });
|
|
15
|
+
Object.defineProperty(exports, "PredicateFilter", { enumerable: true, get: function () { return index_js_2.PredicateFilter; } });
|
|
16
|
+
Object.defineProperty(exports, "LevelFilter", { enumerable: true, get: function () { return index_js_2.LevelFilter; } });
|
|
17
|
+
Object.defineProperty(exports, "PrefixFilter", { enumerable: true, get: function () { return index_js_2.PrefixFilter; } });
|
|
18
|
+
Object.defineProperty(exports, "MetadataFilter", { enumerable: true, get: function () { return index_js_2.MetadataFilter; } });
|
|
19
|
+
Object.defineProperty(exports, "FieldFilter", { enumerable: true, get: function () { return index_js_2.FieldFilter; } });
|
|
20
|
+
const index_js_3 = require("./aggregation/index.js");
|
|
21
|
+
Object.defineProperty(exports, "BatchAggregator", { enumerable: true, get: function () { return index_js_3.BatchAggregator; } });
|
|
22
|
+
Object.defineProperty(exports, "TimeBasedAggregator", { enumerable: true, get: function () { return index_js_3.TimeBasedAggregator; } });
|
|
23
|
+
Object.defineProperty(exports, "CompositeAggregator", { enumerable: true, get: function () { return index_js_3.CompositeAggregator; } });
|
|
24
|
+
const index_js_4 = require("./structured/index.js");
|
|
25
|
+
Object.defineProperty(exports, "MetadataEnricher", { enumerable: true, get: function () { return index_js_4.MetadataEnricher; } });
|
|
26
|
+
Object.defineProperty(exports, "LogEnrichmentPipeline", { enumerable: true, get: function () { return index_js_4.LogEnrichmentPipeline; } });
|
|
10
27
|
// Configure default transports to maintain backward compatibility
|
|
11
28
|
Logger_js_1.Logger.defaultTransportsFactory = (isProd) => {
|
|
12
29
|
if (isProd) {
|