@hamak/logging-impl 0.4.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/dist/core/context-logger.d.ts +26 -0
- package/dist/core/context-logger.d.ts.map +1 -0
- package/dist/core/context-logger.js +82 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +2 -0
- package/dist/core/log-manager.d.ts +41 -0
- package/dist/core/log-manager.d.ts.map +1 -0
- package/dist/core/log-manager.js +241 -0
- package/dist/es2015/core/context-logger.js +74 -0
- package/dist/es2015/core/index.js +2 -0
- package/dist/es2015/core/log-manager.js +255 -0
- package/dist/es2015/formatters/dev-formatter.js +152 -0
- package/dist/es2015/formatters/index.js +2 -0
- package/dist/es2015/formatters/json-formatter.js +69 -0
- package/dist/es2015/index.js +19 -0
- package/dist/es2015/plugin/index.js +1 -0
- package/dist/es2015/plugin/logging-plugin-factory.js +118 -0
- package/dist/es2015/transports/console-transport.js +41 -0
- package/dist/es2015/transports/index.js +1 -0
- package/dist/es2015/utils/console-interceptor.js +105 -0
- package/dist/es2015/utils/event-emitter.js +39 -0
- package/dist/es2015/utils/index.js +2 -0
- package/dist/formatters/dev-formatter.d.ts +20 -0
- package/dist/formatters/dev-formatter.d.ts.map +1 -0
- package/dist/formatters/dev-formatter.js +140 -0
- package/dist/formatters/index.d.ts +3 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +2 -0
- package/dist/formatters/json-formatter.d.ts +24 -0
- package/dist/formatters/json-formatter.d.ts.map +1 -0
- package/dist/formatters/json-formatter.js +65 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +1 -0
- package/dist/plugin/logging-plugin-factory.d.ts +37 -0
- package/dist/plugin/logging-plugin-factory.d.ts.map +1 -0
- package/dist/plugin/logging-plugin-factory.js +112 -0
- package/dist/transports/console-transport.d.ts +16 -0
- package/dist/transports/console-transport.d.ts.map +1 -0
- package/dist/transports/console-transport.js +40 -0
- package/dist/transports/index.d.ts +2 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +1 -0
- package/dist/utils/console-interceptor.d.ts +36 -0
- package/dist/utils/console-interceptor.d.ts.map +1 -0
- package/dist/utils/console-interceptor.js +105 -0
- package/dist/utils/event-emitter.d.ts +11 -0
- package/dist/utils/event-emitter.d.ts.map +1 -0
- package/dist/utils/event-emitter.js +39 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/package.json +54 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { ContextLogger } from './context-logger.js';
|
|
11
|
+
import { EventEmitter } from '../utils/event-emitter.js';
|
|
12
|
+
/**
|
|
13
|
+
* Central log manager implementation
|
|
14
|
+
*
|
|
15
|
+
* Manages transports, creates loggers, and dispatches log entries.
|
|
16
|
+
*/
|
|
17
|
+
export class LogManager {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
var _a, _b, _c, _d, _e, _f;
|
|
20
|
+
this.transports = new Map();
|
|
21
|
+
this.transportConfigs = new Map();
|
|
22
|
+
this.loggers = new Map();
|
|
23
|
+
this.buffer = [];
|
|
24
|
+
this.eventEmitter = new EventEmitter();
|
|
25
|
+
this.config = {
|
|
26
|
+
globalLevel: (_a = config.globalLevel) !== null && _a !== void 0 ? _a : 2, // LogLevel.INFO
|
|
27
|
+
bufferSize: (_b = config.bufferSize) !== null && _b !== void 0 ? _b : 1000,
|
|
28
|
+
flushInterval: (_c = config.flushInterval) !== null && _c !== void 0 ? _c : 5000,
|
|
29
|
+
defaultTransports: (_d = config.defaultTransports) !== null && _d !== void 0 ? _d : [],
|
|
30
|
+
pluginLevels: (_e = config.pluginLevels) !== null && _e !== void 0 ? _e : {},
|
|
31
|
+
moduleLevels: (_f = config.moduleLevels) !== null && _f !== void 0 ? _f : {},
|
|
32
|
+
};
|
|
33
|
+
if (this.config.flushInterval > 0) {
|
|
34
|
+
this.startFlushTimer();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
registerTransport(transport, config) {
|
|
38
|
+
this.transports.set(transport.id, transport);
|
|
39
|
+
if (config) {
|
|
40
|
+
this.transportConfigs.set(transport.id, Object.assign({ enabled: true }, config));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.transportConfigs.set(transport.id, {
|
|
44
|
+
id: transport.id,
|
|
45
|
+
enabled: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
unregisterTransport(id) {
|
|
50
|
+
const transport = this.transports.get(id);
|
|
51
|
+
if (transport && transport.close) {
|
|
52
|
+
transport.close().catch((error) => {
|
|
53
|
+
this.handleTransportError(transport, error);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
this.transports.delete(id);
|
|
57
|
+
this.transportConfigs.delete(id);
|
|
58
|
+
}
|
|
59
|
+
getTransport(id) {
|
|
60
|
+
return this.transports.get(id);
|
|
61
|
+
}
|
|
62
|
+
listTransports() {
|
|
63
|
+
return Array.from(this.transports.values());
|
|
64
|
+
}
|
|
65
|
+
createLogger(context) {
|
|
66
|
+
const level = this.resolveLevel(context);
|
|
67
|
+
return new ContextLogger(this, context, level);
|
|
68
|
+
}
|
|
69
|
+
getLogger(context) {
|
|
70
|
+
const key = this.createCacheKey(context);
|
|
71
|
+
let logger = this.loggers.get(key);
|
|
72
|
+
if (!logger) {
|
|
73
|
+
logger = this.createLogger(context);
|
|
74
|
+
this.loggers.set(key, logger);
|
|
75
|
+
}
|
|
76
|
+
return logger;
|
|
77
|
+
}
|
|
78
|
+
setGlobalLevel(level) {
|
|
79
|
+
this.config.globalLevel = level;
|
|
80
|
+
}
|
|
81
|
+
setPluginLevel(plugin, level) {
|
|
82
|
+
this.config.pluginLevels[plugin] = level;
|
|
83
|
+
}
|
|
84
|
+
setModuleLevel(module, level) {
|
|
85
|
+
this.config.moduleLevels[module] = level;
|
|
86
|
+
}
|
|
87
|
+
flush() {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
const entries = this.buffer.splice(0);
|
|
90
|
+
if (entries.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
yield Promise.all(entries.map(entry => this.dispatchToTransports(entry)));
|
|
94
|
+
yield Promise.all(Array.from(this.transports.values())
|
|
95
|
+
.filter(t => t.flush)
|
|
96
|
+
.map(t => {
|
|
97
|
+
try {
|
|
98
|
+
return t.flush();
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
this.handleTransportError(t, error);
|
|
102
|
+
return Promise.resolve();
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
this.eventEmitter.emit('flush', entries.length);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
clearBuffer() {
|
|
109
|
+
this.buffer = [];
|
|
110
|
+
}
|
|
111
|
+
on(event, handler) {
|
|
112
|
+
this.eventEmitter.on(event, handler);
|
|
113
|
+
}
|
|
114
|
+
off(event, handler) {
|
|
115
|
+
this.eventEmitter.off(event, handler);
|
|
116
|
+
}
|
|
117
|
+
dispatch(entry) {
|
|
118
|
+
this.buffer.push(entry);
|
|
119
|
+
this.eventEmitter.emit('log', entry);
|
|
120
|
+
// Immediate dispatch for high-priority logs
|
|
121
|
+
if (entry.level >= 4) { // LogLevel.ERROR
|
|
122
|
+
this.dispatchToTransports(entry).catch(error => {
|
|
123
|
+
console.error('[LogManager] Error dispatching log entry:', error);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// Flush if buffer is full
|
|
127
|
+
if (this.buffer.length >= this.config.bufferSize) {
|
|
128
|
+
this.flush().catch(error => {
|
|
129
|
+
console.error('[LogManager] Error flushing buffer:', error);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Clean up resources
|
|
135
|
+
*/
|
|
136
|
+
destroy() {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
if (this.flushTimer) {
|
|
139
|
+
clearInterval(this.flushTimer);
|
|
140
|
+
this.flushTimer = undefined;
|
|
141
|
+
}
|
|
142
|
+
yield this.flush();
|
|
143
|
+
yield Promise.all(Array.from(this.transports.values())
|
|
144
|
+
.filter(t => t.close)
|
|
145
|
+
.map(t => t.close()));
|
|
146
|
+
this.transports.clear();
|
|
147
|
+
this.transportConfigs.clear();
|
|
148
|
+
this.loggers.clear();
|
|
149
|
+
this.eventEmitter.clear();
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
dispatchToTransports(entry) {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
const promises = [];
|
|
155
|
+
for (const [id, transport] of this.transports) {
|
|
156
|
+
const config = this.transportConfigs.get(id);
|
|
157
|
+
if (!this.shouldDispatch(entry, config, transport)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const result = transport.log(entry);
|
|
162
|
+
if (result instanceof Promise) {
|
|
163
|
+
promises.push(result.catch(error => {
|
|
164
|
+
this.handleTransportError(transport, error);
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
this.handleTransportError(transport, error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
yield Promise.all(promises);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
shouldDispatch(entry, config, transport) {
|
|
176
|
+
var _a;
|
|
177
|
+
if (config && config.enabled === false) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if ((config === null || config === void 0 ? void 0 : config.minLevel) !== undefined && entry.level < config.minLevel) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if ((config === null || config === void 0 ? void 0 : config.maxLevel) !== undefined && entry.level > config.maxLevel) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
if ((config === null || config === void 0 ? void 0 : config.plugins) && config.plugins.length > 0) {
|
|
187
|
+
if (!entry.context.plugin || !config.plugins.includes(entry.context.plugin)) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if ((config === null || config === void 0 ? void 0 : config.modules) && config.modules.length > 0) {
|
|
192
|
+
if (!entry.context.module || !config.modules.includes(entry.context.module)) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if ((config === null || config === void 0 ? void 0 : config.tags) && config.tags.length > 0) {
|
|
197
|
+
if (!((_a = entry.metadata) === null || _a === void 0 ? void 0 : _a.tags) || !config.tags.some(tag => entry.metadata.tags.includes(tag))) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if ((config === null || config === void 0 ? void 0 : config.filter) && !config.filter(entry)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
if ((transport === null || transport === void 0 ? void 0 : transport.shouldLog) && !transport.shouldLog(entry)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
resolveLevel(context) {
|
|
210
|
+
if (context === null || context === void 0 ? void 0 : context.plugin) {
|
|
211
|
+
const pluginLevel = this.config.pluginLevels[context.plugin];
|
|
212
|
+
if (pluginLevel !== undefined) {
|
|
213
|
+
return pluginLevel;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (context === null || context === void 0 ? void 0 : context.module) {
|
|
217
|
+
const moduleLevel = this.config.moduleLevels[context.module];
|
|
218
|
+
if (moduleLevel !== undefined) {
|
|
219
|
+
return moduleLevel;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return this.config.globalLevel;
|
|
223
|
+
}
|
|
224
|
+
createCacheKey(context) {
|
|
225
|
+
if (!context) {
|
|
226
|
+
return '__default__';
|
|
227
|
+
}
|
|
228
|
+
return JSON.stringify({
|
|
229
|
+
plugin: context.plugin,
|
|
230
|
+
module: context.module,
|
|
231
|
+
component: context.component,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
startFlushTimer() {
|
|
235
|
+
this.flushTimer = setInterval(() => {
|
|
236
|
+
this.flush().catch(error => {
|
|
237
|
+
console.error('[LogManager] Error in flush timer:', error);
|
|
238
|
+
});
|
|
239
|
+
}, this.config.flushInterval);
|
|
240
|
+
}
|
|
241
|
+
handleTransportError(transport, error) {
|
|
242
|
+
this.eventEmitter.emit('error', { transport: transport.id, error });
|
|
243
|
+
if (transport.onError) {
|
|
244
|
+
try {
|
|
245
|
+
transport.onError(error);
|
|
246
|
+
}
|
|
247
|
+
catch (handlerError) {
|
|
248
|
+
console.error('[LogManager] Error in transport error handler:', handlerError);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
console.error(`[LogManager] Transport '${transport.id}' error:`, error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Development formatter with colors and emojis
|
|
14
|
+
*
|
|
15
|
+
* Provides rich, human-readable console output for development.
|
|
16
|
+
*/
|
|
17
|
+
export class DevFormatter {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
var _a, _b, _c, _d;
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.config = {
|
|
22
|
+
colors: (_a = config.colors) !== null && _a !== void 0 ? _a : true,
|
|
23
|
+
emojis: (_b = config.emojis) !== null && _b !== void 0 ? _b : true,
|
|
24
|
+
timestamps: (_c = config.timestamps) !== null && _c !== void 0 ? _c : true,
|
|
25
|
+
showContext: (_d = config.showContext) !== null && _d !== void 0 ? _d : true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
format(entry) {
|
|
29
|
+
const parts = [];
|
|
30
|
+
// Timestamp
|
|
31
|
+
if (this.config.timestamps) {
|
|
32
|
+
parts.push(this.formatTimestamp(entry.timestamp));
|
|
33
|
+
}
|
|
34
|
+
// Level with emoji and color
|
|
35
|
+
parts.push(this.formatLevel(entry.level));
|
|
36
|
+
// Context (plugin/module/component)
|
|
37
|
+
if (this.config.showContext) {
|
|
38
|
+
const context = this.formatContext(entry.context);
|
|
39
|
+
if (context) {
|
|
40
|
+
parts.push(context);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Message
|
|
44
|
+
parts.push(entry.message);
|
|
45
|
+
// Metadata
|
|
46
|
+
const metadata = this.formatMetadata(entry.metadata);
|
|
47
|
+
if (metadata) {
|
|
48
|
+
parts.push(metadata);
|
|
49
|
+
}
|
|
50
|
+
return parts.join(' ');
|
|
51
|
+
}
|
|
52
|
+
formatBatch(entries) {
|
|
53
|
+
return entries.map(e => this.format(e)).join('\n');
|
|
54
|
+
}
|
|
55
|
+
formatTimestamp(timestamp) {
|
|
56
|
+
const date = new Date(timestamp);
|
|
57
|
+
const time = date.toISOString().split('T')[1].split('.')[0];
|
|
58
|
+
return this.config.colors ? `\x1b[90m${time}\x1b[0m` : time;
|
|
59
|
+
}
|
|
60
|
+
formatLevel(level) {
|
|
61
|
+
const emoji = this.config.emojis ? this.getLevelEmoji(level) : '';
|
|
62
|
+
const label = this.getLevelLabel(level);
|
|
63
|
+
const colored = this.config.colors ? this.colorize(label, level) : label;
|
|
64
|
+
return emoji ? `${emoji} ${colored}` : colored;
|
|
65
|
+
}
|
|
66
|
+
getLevelEmoji(level) {
|
|
67
|
+
switch (level) {
|
|
68
|
+
case 0: return '🔍'; // TRACE
|
|
69
|
+
case 1: return '🐛'; // DEBUG
|
|
70
|
+
case 2: return 'ℹ️ '; // INFO
|
|
71
|
+
case 3: return '⚠️ '; // WARN
|
|
72
|
+
case 4: return '❌'; // ERROR
|
|
73
|
+
case 5: return '💀'; // FATAL
|
|
74
|
+
default: return '';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
getLevelLabel(level) {
|
|
78
|
+
switch (level) {
|
|
79
|
+
case 0: return 'TRACE';
|
|
80
|
+
case 1: return 'DEBUG';
|
|
81
|
+
case 2: return 'INFO ';
|
|
82
|
+
case 3: return 'WARN ';
|
|
83
|
+
case 4: return 'ERROR';
|
|
84
|
+
case 5: return 'FATAL';
|
|
85
|
+
default: return 'UNKNW';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
colorize(text, level) {
|
|
89
|
+
const colors = {
|
|
90
|
+
0: '\x1b[90m', // TRACE - Gray
|
|
91
|
+
1: '\x1b[36m', // DEBUG - Cyan
|
|
92
|
+
2: '\x1b[32m', // INFO - Green
|
|
93
|
+
3: '\x1b[33m', // WARN - Yellow
|
|
94
|
+
4: '\x1b[31m', // ERROR - Red
|
|
95
|
+
5: '\x1b[35m', // FATAL - Magenta
|
|
96
|
+
};
|
|
97
|
+
const color = colors[level] || '\x1b[0m';
|
|
98
|
+
const reset = '\x1b[0m';
|
|
99
|
+
return `${color}${text}${reset}`;
|
|
100
|
+
}
|
|
101
|
+
formatContext(context) {
|
|
102
|
+
const parts = [];
|
|
103
|
+
if (context.plugin) {
|
|
104
|
+
parts.push(this.config.colors
|
|
105
|
+
? `\x1b[35m[${context.plugin}]\x1b[0m`
|
|
106
|
+
: `[${context.plugin}]`);
|
|
107
|
+
}
|
|
108
|
+
if (context.module) {
|
|
109
|
+
parts.push(this.config.colors
|
|
110
|
+
? `\x1b[34m{${context.module}}\x1b[0m`
|
|
111
|
+
: `{${context.module}}`);
|
|
112
|
+
}
|
|
113
|
+
if (context.component) {
|
|
114
|
+
parts.push(this.config.colors
|
|
115
|
+
? `\x1b[36m<${context.component}>\x1b[0m`
|
|
116
|
+
: `<${context.component}>`);
|
|
117
|
+
}
|
|
118
|
+
if (context.operation) {
|
|
119
|
+
parts.push(this.config.colors
|
|
120
|
+
? `\x1b[90m(${context.operation})\x1b[0m`
|
|
121
|
+
: `(${context.operation})`);
|
|
122
|
+
}
|
|
123
|
+
return parts.join(' ');
|
|
124
|
+
}
|
|
125
|
+
formatMetadata(metadata) {
|
|
126
|
+
if (!metadata || Object.keys(metadata).length === 0) {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
const { error, stack, tags, duration } = metadata, rest = __rest(metadata, ["error", "stack", "tags", "duration"]);
|
|
130
|
+
const parts = [];
|
|
131
|
+
// Duration
|
|
132
|
+
if (duration !== undefined) {
|
|
133
|
+
const color = this.config.colors ? '\x1b[33m' : '';
|
|
134
|
+
const reset = this.config.colors ? '\x1b[0m' : '';
|
|
135
|
+
parts.push(`${color}⏱ ${duration}ms${reset}`);
|
|
136
|
+
}
|
|
137
|
+
// Tags
|
|
138
|
+
if (tags && tags.length > 0) {
|
|
139
|
+
const tagString = tags.map(t => `#${t}`).join(' ');
|
|
140
|
+
const color = this.config.colors ? '\x1b[90m' : '';
|
|
141
|
+
const reset = this.config.colors ? '\x1b[0m' : '';
|
|
142
|
+
parts.push(`${color}${tagString}${reset}`);
|
|
143
|
+
}
|
|
144
|
+
// Other metadata
|
|
145
|
+
if (Object.keys(rest).length > 0) {
|
|
146
|
+
const color = this.config.colors ? '\x1b[90m' : '';
|
|
147
|
+
const reset = this.config.colors ? '\x1b[0m' : '';
|
|
148
|
+
parts.push(`${color}${JSON.stringify(rest)}${reset}`);
|
|
149
|
+
}
|
|
150
|
+
return parts.length > 0 ? parts.join(' ') : '';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* JSON formatter for structured logging
|
|
14
|
+
*
|
|
15
|
+
* Outputs logs as JSON for easy parsing by log aggregation tools.
|
|
16
|
+
*/
|
|
17
|
+
export class JsonFormatter {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.config = Object.assign({ pretty: (_a = config.pretty) !== null && _a !== void 0 ? _a : false, includeStack: (_b = config.includeStack) !== null && _b !== void 0 ? _b : true }, config);
|
|
22
|
+
}
|
|
23
|
+
format(entry) {
|
|
24
|
+
const logObject = this.toObject(entry);
|
|
25
|
+
return this.config.pretty
|
|
26
|
+
? JSON.stringify(logObject, null, 2)
|
|
27
|
+
: JSON.stringify(logObject);
|
|
28
|
+
}
|
|
29
|
+
formatBatch(entries) {
|
|
30
|
+
const logObjects = entries.map(e => this.toObject(e));
|
|
31
|
+
return this.config.pretty
|
|
32
|
+
? JSON.stringify(logObjects, null, 2)
|
|
33
|
+
: JSON.stringify(logObjects);
|
|
34
|
+
}
|
|
35
|
+
toObject(entry) {
|
|
36
|
+
const obj = {
|
|
37
|
+
timestamp: entry.timestamp,
|
|
38
|
+
level: this.getLevelName(entry.level),
|
|
39
|
+
message: entry.message,
|
|
40
|
+
};
|
|
41
|
+
// Add context
|
|
42
|
+
if (entry.context && Object.keys(entry.context).length > 0) {
|
|
43
|
+
obj.context = entry.context;
|
|
44
|
+
}
|
|
45
|
+
// Add metadata
|
|
46
|
+
if (entry.metadata) {
|
|
47
|
+
const _a = entry.metadata, { error, stack } = _a, rest = __rest(_a, ["error", "stack"]);
|
|
48
|
+
if (Object.keys(rest).length > 0) {
|
|
49
|
+
obj.metadata = rest;
|
|
50
|
+
}
|
|
51
|
+
// Handle error separately
|
|
52
|
+
if (error) {
|
|
53
|
+
obj.error = Object.assign({ name: error.name, message: error.message }, (this.config.includeStack && stack ? { stack } : {}));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
58
|
+
getLevelName(level) {
|
|
59
|
+
switch (level) {
|
|
60
|
+
case 0: return 'TRACE';
|
|
61
|
+
case 1: return 'DEBUG';
|
|
62
|
+
case 2: return 'INFO';
|
|
63
|
+
case 3: return 'WARN';
|
|
64
|
+
case 4: return 'ERROR';
|
|
65
|
+
case 5: return 'FATAL';
|
|
66
|
+
default: return 'UNKNOWN';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/logging-impl
|
|
3
|
+
*
|
|
4
|
+
* Core implementation of the pluggable logging system.
|
|
5
|
+
* Provides LogManager, ContextLogger, transports, and formatters.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
// Export core implementations
|
|
10
|
+
export * from './core/index.js';
|
|
11
|
+
// Export transports
|
|
12
|
+
export * from './transports/index.js';
|
|
13
|
+
// Export formatters
|
|
14
|
+
export * from './formatters/index.js';
|
|
15
|
+
// Export plugin factory
|
|
16
|
+
export * from './plugin/index.js';
|
|
17
|
+
// Export utilities
|
|
18
|
+
export * from './utils/index.js';
|
|
19
|
+
export { LOG_MANAGER_TOKEN, LOGGER_TOKEN, LOG_CONFIG_TOKEN, } from '@hamak/logging-api';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './logging-plugin-factory.js';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { LOG_MANAGER_TOKEN, LOGGER_TOKEN, LOG_CONFIG_TOKEN } from '@hamak/logging-api';
|
|
11
|
+
import { LogManager } from '../core/log-manager.js';
|
|
12
|
+
import { ConsoleTransport } from '../transports/console-transport.js';
|
|
13
|
+
import { DevFormatter } from '../formatters/dev-formatter.js';
|
|
14
|
+
import { JsonFormatter } from '../formatters/json-formatter.js';
|
|
15
|
+
import { ConsoleInterceptor } from '../utils/console-interceptor.js';
|
|
16
|
+
/**
|
|
17
|
+
* Create a logging plugin for the microkernel
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createLoggingPlugin } from '@hamak/logging-impl';
|
|
22
|
+
*
|
|
23
|
+
* const loggingPlugin = createLoggingPlugin({
|
|
24
|
+
* globalLevel: LogLevel.DEBUG,
|
|
25
|
+
* pluginLevels: {
|
|
26
|
+
* 'my-plugin': LogLevel.TRACE
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const host = createHost({
|
|
31
|
+
* plugins: [loggingPlugin, myPlugin]
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function createLoggingPlugin(config = {}) {
|
|
36
|
+
let logManager;
|
|
37
|
+
let rootLogger;
|
|
38
|
+
let consoleInterceptor;
|
|
39
|
+
// Determine environment
|
|
40
|
+
const isDevelopment = typeof process !== 'undefined'
|
|
41
|
+
? process.env.NODE_ENV === 'development'
|
|
42
|
+
: true;
|
|
43
|
+
const defaultConfig = Object.assign({ globalLevel: isDevelopment ? 1 : 2, bufferSize: 1000, flushInterval: 5000, devFormatter: isDevelopment, interceptConsole: true, pluginLevels: {}, moduleLevels: {} }, config);
|
|
44
|
+
return {
|
|
45
|
+
initialize(ctx) {
|
|
46
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
// Create log manager
|
|
48
|
+
logManager = new LogManager(defaultConfig);
|
|
49
|
+
rootLogger = logManager.createLogger({ plugin: 'logging', module: 'plugin' });
|
|
50
|
+
// Register console transport
|
|
51
|
+
const formatter = defaultConfig.devFormatter
|
|
52
|
+
? new DevFormatter({
|
|
53
|
+
colors: true,
|
|
54
|
+
emojis: true,
|
|
55
|
+
timestamps: true,
|
|
56
|
+
showContext: true,
|
|
57
|
+
})
|
|
58
|
+
: new JsonFormatter({ pretty: isDevelopment });
|
|
59
|
+
logManager.registerTransport(new ConsoleTransport(formatter), {
|
|
60
|
+
id: 'console',
|
|
61
|
+
enabled: true,
|
|
62
|
+
minLevel: defaultConfig.globalLevel,
|
|
63
|
+
});
|
|
64
|
+
// Register services via DI
|
|
65
|
+
ctx.provide({ provide: LOG_MANAGER_TOKEN, useValue: logManager });
|
|
66
|
+
ctx.provide({ provide: LOG_CONFIG_TOKEN, useValue: defaultConfig });
|
|
67
|
+
// Provide logger factory
|
|
68
|
+
ctx.provide({
|
|
69
|
+
provide: LOGGER_TOKEN,
|
|
70
|
+
useFactory: (pluginName) => {
|
|
71
|
+
return logManager.createLogger({ plugin: pluginName });
|
|
72
|
+
},
|
|
73
|
+
deps: [],
|
|
74
|
+
});
|
|
75
|
+
rootLogger.info('Logging plugin initialized', {
|
|
76
|
+
globalLevel: defaultConfig.globalLevel,
|
|
77
|
+
formatter: defaultConfig.devFormatter ? 'dev' : 'json',
|
|
78
|
+
tags: ['initialization'],
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
activate(ctx) {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
// Bridge microkernel events to logging
|
|
85
|
+
ctx.hooks.on('plugin:error', ({ plugin, error }) => {
|
|
86
|
+
const logger = logManager.createLogger({ plugin });
|
|
87
|
+
logger.error('Plugin error', error);
|
|
88
|
+
});
|
|
89
|
+
ctx.hooks.on('host:activated', () => {
|
|
90
|
+
rootLogger.info('Microkernel host activated', { tags: ['lifecycle'] });
|
|
91
|
+
});
|
|
92
|
+
// Intercept console methods if enabled
|
|
93
|
+
if (defaultConfig.interceptConsole) {
|
|
94
|
+
consoleInterceptor = new ConsoleInterceptor(logManager);
|
|
95
|
+
consoleInterceptor.intercept();
|
|
96
|
+
rootLogger.debug('Console interception enabled', { tags: ['console'] });
|
|
97
|
+
}
|
|
98
|
+
// Emit logging ready event
|
|
99
|
+
ctx.hooks.emit('logging:ready', { logManager });
|
|
100
|
+
rootLogger.info('Logging plugin activated', {
|
|
101
|
+
tags: ['lifecycle'],
|
|
102
|
+
interceptConsole: defaultConfig.interceptConsole,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
deactivate() {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
rootLogger.info('Logging plugin deactivating', { tags: ['lifecycle'] });
|
|
109
|
+
// Restore original console methods
|
|
110
|
+
if (consoleInterceptor) {
|
|
111
|
+
consoleInterceptor.restore();
|
|
112
|
+
rootLogger.debug('Console interception disabled', { tags: ['console'] });
|
|
113
|
+
}
|
|
114
|
+
yield logManager.destroy();
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console transport
|
|
3
|
+
*
|
|
4
|
+
* Outputs logs to the browser console or Node.js console.
|
|
5
|
+
* Supports formatting via pluggable formatters.
|
|
6
|
+
*/
|
|
7
|
+
export class ConsoleTransport {
|
|
8
|
+
constructor(formatter) {
|
|
9
|
+
this.formatter = formatter;
|
|
10
|
+
this.id = 'console';
|
|
11
|
+
this.name = 'Console Transport';
|
|
12
|
+
}
|
|
13
|
+
log(entry) {
|
|
14
|
+
var _a;
|
|
15
|
+
const formatted = this.formatter.format(entry);
|
|
16
|
+
switch (entry.level) {
|
|
17
|
+
case 0: // TRACE
|
|
18
|
+
case 1: // DEBUG
|
|
19
|
+
console.debug(formatted);
|
|
20
|
+
break;
|
|
21
|
+
case 2: // INFO
|
|
22
|
+
console.info(formatted);
|
|
23
|
+
break;
|
|
24
|
+
case 3: // WARN
|
|
25
|
+
console.warn(formatted);
|
|
26
|
+
break;
|
|
27
|
+
case 4: // ERROR
|
|
28
|
+
case 5: // FATAL
|
|
29
|
+
console.error(formatted);
|
|
30
|
+
if ((_a = entry.metadata) === null || _a === void 0 ? void 0 : _a.error) {
|
|
31
|
+
console.error(entry.metadata.error);
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
console.log(formatted);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
onError(error) {
|
|
39
|
+
console.error('[ConsoleTransport] Error:', error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './console-transport.js';
|