@c9up/spectrum 0.1.3
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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/Logger.d.ts +34 -0
- package/dist/Logger.d.ts.map +1 -0
- package/dist/Logger.js +72 -0
- package/dist/Logger.js.map +1 -0
- package/dist/RustLogBridge.d.ts +22 -0
- package/dist/RustLogBridge.d.ts.map +1 -0
- package/dist/RustLogBridge.js +124 -0
- package/dist/RustLogBridge.js.map +1 -0
- package/dist/SpectrumProvider.d.ts +19 -0
- package/dist/SpectrumProvider.d.ts.map +1 -0
- package/dist/SpectrumProvider.js +35 -0
- package/dist/SpectrumProvider.js.map +1 -0
- package/dist/channels/ConsoleChannel.d.ts +13 -0
- package/dist/channels/ConsoleChannel.d.ts.map +1 -0
- package/dist/channels/ConsoleChannel.js +89 -0
- package/dist/channels/ConsoleChannel.js.map +1 -0
- package/dist/channels/FileChannel.d.ts +23 -0
- package/dist/channels/FileChannel.d.ts.map +1 -0
- package/dist/channels/FileChannel.js +150 -0
- package/dist/channels/FileChannel.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +10 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +11 -0
- package/dist/configure.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/services/main.d.ts +18 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +31 -0
- package/dist/services/main.js.map +1 -0
- package/dist/testing/FakeLogger.d.ts +28 -0
- package/dist/testing/FakeLogger.d.ts.map +1 -0
- package/dist/testing/FakeLogger.js +111 -0
- package/dist/testing/FakeLogger.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/src/Logger.ts +99 -0
- package/src/RustLogBridge.ts +142 -0
- package/src/SpectrumProvider.ts +52 -0
- package/src/channels/ConsoleChannel.ts +100 -0
- package/src/channels/FileChannel.ts +170 -0
- package/src/config.ts +7 -0
- package/src/configure.ts +23 -0
- package/src/index.ts +13 -0
- package/src/services/main.ts +39 -0
- package/src/testing/FakeLogger.ts +140 -0
- package/src/types.ts +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 C9up
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @c9up/spectrum
|
|
2
|
+
|
|
3
|
+
Structured logging for Node.js. Levels, channels, correlation IDs, per-module overrides.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { Logger, ConsoleChannel } from '@c9up/spectrum'
|
|
9
|
+
|
|
10
|
+
const logger = new Logger({
|
|
11
|
+
level: 'info',
|
|
12
|
+
channels: [new ConsoleChannel('pretty')],
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
logger.info('Server started', { port: 3000 })
|
|
16
|
+
logger.child({ module: 'db', correlationId: 'abc-123' }).debug('Query executed')
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- 6 log levels: trace, debug, info, warn, error, fatal
|
|
22
|
+
- ConsoleChannel (pretty + JSON formats)
|
|
23
|
+
- Per-module level overrides
|
|
24
|
+
- Child loggers with scoped module/correlationId
|
|
25
|
+
- error/fatal → stderr, others → stdout
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
MIT
|
package/dist/Logger.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectrum Logger — structured logging with levels and correlation ID.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR54, FR55, FR56, FR58
|
|
5
|
+
*/
|
|
6
|
+
import type { LogConfig, LogLevel } from "./types.js";
|
|
7
|
+
export type { LogLevel };
|
|
8
|
+
export declare class Logger {
|
|
9
|
+
private config;
|
|
10
|
+
private module;
|
|
11
|
+
private correlationId?;
|
|
12
|
+
constructor(config: LogConfig, module?: string, correlationId?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Create a child logger scoped to a module and/or correlation ID.
|
|
15
|
+
* This is the preferred way to set correlation ID — creates an immutable copy.
|
|
16
|
+
*/
|
|
17
|
+
child(options: {
|
|
18
|
+
module?: string;
|
|
19
|
+
correlationId?: string;
|
|
20
|
+
}): Logger;
|
|
21
|
+
/**
|
|
22
|
+
* Set the correlation ID on THIS instance.
|
|
23
|
+
* Prefer child() for per-request scoping to avoid shared-state mutation.
|
|
24
|
+
*/
|
|
25
|
+
setCorrelationId(id: string): void;
|
|
26
|
+
trace(message: string, data?: Record<string, unknown>): void;
|
|
27
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
28
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
29
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
30
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
31
|
+
fatal(message: string, data?: Record<string, unknown>): void;
|
|
32
|
+
private log;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=Logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGhE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAC,CAAS;gBAEnB,MAAM,EAAE,SAAS,EAAE,MAAM,SAAQ,EAAE,aAAa,CAAC,EAAE,MAAM;IAMrE;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAQnE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIlC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,OAAO,CAAC,GAAG;CAgCX"}
|
package/dist/Logger.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectrum Logger — structured logging with levels and correlation ID.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR54, FR55, FR56, FR58
|
|
5
|
+
*/
|
|
6
|
+
import { LOG_LEVEL_ORDER } from "./types.js";
|
|
7
|
+
export class Logger {
|
|
8
|
+
config;
|
|
9
|
+
module;
|
|
10
|
+
correlationId;
|
|
11
|
+
constructor(config, module = "app", correlationId) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.module = module;
|
|
14
|
+
this.correlationId = correlationId;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a child logger scoped to a module and/or correlation ID.
|
|
18
|
+
* This is the preferred way to set correlation ID — creates an immutable copy.
|
|
19
|
+
*/
|
|
20
|
+
child(options) {
|
|
21
|
+
return new Logger(this.config, options.module ?? this.module, options.correlationId ?? this.correlationId);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Set the correlation ID on THIS instance.
|
|
25
|
+
* Prefer child() for per-request scoping to avoid shared-state mutation.
|
|
26
|
+
*/
|
|
27
|
+
setCorrelationId(id) {
|
|
28
|
+
this.correlationId = id;
|
|
29
|
+
}
|
|
30
|
+
trace(message, data) {
|
|
31
|
+
this.log("trace", message, data);
|
|
32
|
+
}
|
|
33
|
+
debug(message, data) {
|
|
34
|
+
this.log("debug", message, data);
|
|
35
|
+
}
|
|
36
|
+
info(message, data) {
|
|
37
|
+
this.log("info", message, data);
|
|
38
|
+
}
|
|
39
|
+
warn(message, data) {
|
|
40
|
+
this.log("warn", message, data);
|
|
41
|
+
}
|
|
42
|
+
error(message, data) {
|
|
43
|
+
this.log("error", message, data);
|
|
44
|
+
}
|
|
45
|
+
fatal(message, data) {
|
|
46
|
+
this.log("fatal", message, data);
|
|
47
|
+
}
|
|
48
|
+
log(level, message, data) {
|
|
49
|
+
const rawEffective = this.config.modules?.[this.module] ?? this.config.level;
|
|
50
|
+
const effectiveLevel = LOG_LEVEL_ORDER[rawEffective] !== undefined ? rawEffective : "info";
|
|
51
|
+
if ((LOG_LEVEL_ORDER[level] ?? 0) < LOG_LEVEL_ORDER[effectiveLevel]) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const entry = {
|
|
55
|
+
level,
|
|
56
|
+
message,
|
|
57
|
+
module: this.module,
|
|
58
|
+
correlationId: this.correlationId,
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
data,
|
|
61
|
+
};
|
|
62
|
+
for (const channel of this.config.channels) {
|
|
63
|
+
try {
|
|
64
|
+
channel.write(entry);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
process.stderr.write(`[Spectrum] Channel '${channel.name}' failed for: ${message} — ${String(err)}\n`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=Logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAI7C,MAAM,OAAO,MAAM;IACV,MAAM,CAAY;IAClB,MAAM,CAAS;IACf,aAAa,CAAU;IAE/B,YAAY,MAAiB,EAAE,MAAM,GAAG,KAAK,EAAE,aAAsB;QACpE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAoD;QACzD,OAAO,IAAI,MAAM,CAChB,IAAI,CAAC,MAAM,EACX,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAC7B,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,CAC3C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,EAAU;QAC1B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAEO,GAAG,CACV,KAAe,EACf,OAAe,EACf,IAA8B;QAE9B,MAAM,YAAY,GACjB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QACzD,MAAM,cAAc,GACnB,eAAe,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC;QACrE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,eAAe,CAAC,cAAc,CAAC,EAAE,CAAC;YACrE,OAAO;QACR,CAAC;QAED,MAAM,KAAK,GAAa;YACvB,KAAK;YACL,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,IAAI;SACJ,CAAC;QAEF,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACJ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,uBAAuB,OAAO,CAAC,IAAI,iBAAiB,OAAO,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAChF,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;CACD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rust Log Bridge — unifies Rust stderr output with Spectrum log entries.
|
|
3
|
+
*
|
|
4
|
+
* Captures Rust log output (which goes to stderr) and re-emits it
|
|
5
|
+
* through the Spectrum Logger so both Rust and TS logs appear in
|
|
6
|
+
* the same stream with the same format.
|
|
7
|
+
*
|
|
8
|
+
* @implements FR55
|
|
9
|
+
*/
|
|
10
|
+
import type { LogChannel, LogEntry } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Parse a Rust log line into a Spectrum LogEntry.
|
|
13
|
+
* Supports common Rust log formats:
|
|
14
|
+
* [INFO ream_http] Server listening on 0.0.0.0:3000
|
|
15
|
+
* [WARN ream_bus] Slow dispatch: 5ms
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseRustLog(line: string): LogEntry | null;
|
|
18
|
+
export declare function createRustLogBridge(channels: LogChannel[]): {
|
|
19
|
+
start: () => void;
|
|
20
|
+
stop: () => void;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=RustLogBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RustLogBridge.d.ts","sourceRoot":"","sources":["../src/RustLogBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAY,MAAM,YAAY,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAuB1D;AAcD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;CACjB,CAmFA"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rust Log Bridge — unifies Rust stderr output with Spectrum log entries.
|
|
3
|
+
*
|
|
4
|
+
* Captures Rust log output (which goes to stderr) and re-emits it
|
|
5
|
+
* through the Spectrum Logger so both Rust and TS logs appear in
|
|
6
|
+
* the same stream with the same format.
|
|
7
|
+
*
|
|
8
|
+
* @implements FR55
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Parse a Rust log line into a Spectrum LogEntry.
|
|
12
|
+
* Supports common Rust log formats:
|
|
13
|
+
* [INFO ream_http] Server listening on 0.0.0.0:3000
|
|
14
|
+
* [WARN ream_bus] Slow dispatch: 5ms
|
|
15
|
+
*/
|
|
16
|
+
export function parseRustLog(line) {
|
|
17
|
+
// Pattern: [LEVEL module] message
|
|
18
|
+
const match = line.match(/^\[(\w+)\s+(\S+)\]\s+(.+)$/);
|
|
19
|
+
if (!match)
|
|
20
|
+
return null;
|
|
21
|
+
const levelMap = {
|
|
22
|
+
TRACE: "trace",
|
|
23
|
+
DEBUG: "debug",
|
|
24
|
+
INFO: "info",
|
|
25
|
+
WARN: "warn",
|
|
26
|
+
ERROR: "error",
|
|
27
|
+
FATAL: "fatal",
|
|
28
|
+
};
|
|
29
|
+
const level = levelMap[match[1].toUpperCase()];
|
|
30
|
+
if (!level)
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
level,
|
|
34
|
+
message: match[3],
|
|
35
|
+
module: match[2].replace(/_/g, "-"),
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a bridge that captures stderr and routes Rust logs to Spectrum channels.
|
|
41
|
+
*
|
|
42
|
+
* Usage:
|
|
43
|
+
* const bridge = createRustLogBridge(logger.config.channels)
|
|
44
|
+
* bridge.start()
|
|
45
|
+
* // ... Rust crates emit to stderr
|
|
46
|
+
* bridge.stop()
|
|
47
|
+
*/
|
|
48
|
+
let activeOriginalWrite;
|
|
49
|
+
let activeBridge;
|
|
50
|
+
export function createRustLogBridge(channels) {
|
|
51
|
+
let bridgingDepth = 0;
|
|
52
|
+
const bridge = {
|
|
53
|
+
start() {
|
|
54
|
+
if (activeBridge === bridge)
|
|
55
|
+
return;
|
|
56
|
+
if (activeBridge) {
|
|
57
|
+
const prev = activeOriginalWrite;
|
|
58
|
+
activeBridge.stop();
|
|
59
|
+
if (prev) {
|
|
60
|
+
process.stderr.write = prev;
|
|
61
|
+
prev("[Spectrum] RustLogBridge replaced — previous bridge stopped. Only one bridge can be active per process.\n");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
activeOriginalWrite = process.stderr.write.bind(process.stderr);
|
|
65
|
+
activeBridge = bridge;
|
|
66
|
+
process.stderr.write = ((chunk, ...args) => {
|
|
67
|
+
const origWrite = activeOriginalWrite;
|
|
68
|
+
if (!origWrite)
|
|
69
|
+
return true;
|
|
70
|
+
if (bridgingDepth > 0) {
|
|
71
|
+
return origWrite(chunk, ...args);
|
|
72
|
+
}
|
|
73
|
+
const str = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
74
|
+
let hadRustLog = false;
|
|
75
|
+
const passthroughLines = [];
|
|
76
|
+
// Try to parse as Rust log — if it matches, route to channels
|
|
77
|
+
for (const line of str.split("\n")) {
|
|
78
|
+
const trimmed = line.trim();
|
|
79
|
+
if (!trimmed)
|
|
80
|
+
continue;
|
|
81
|
+
const entry = parseRustLog(trimmed);
|
|
82
|
+
if (entry) {
|
|
83
|
+
hadRustLog = true;
|
|
84
|
+
for (const channel of channels) {
|
|
85
|
+
bridgingDepth++;
|
|
86
|
+
try {
|
|
87
|
+
channel.write(entry);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* ignore */
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
bridgingDepth--;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
passthroughLines.push(line);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Preserve original chunk when we didn't intercept any Rust logs.
|
|
102
|
+
if (!hadRustLog) {
|
|
103
|
+
return origWrite(chunk, ...args);
|
|
104
|
+
}
|
|
105
|
+
for (const line of passthroughLines) {
|
|
106
|
+
origWrite(`${line}\n`);
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
stop() {
|
|
112
|
+
if (activeBridge !== bridge)
|
|
113
|
+
return;
|
|
114
|
+
if (activeOriginalWrite) {
|
|
115
|
+
process.stderr.write =
|
|
116
|
+
activeOriginalWrite;
|
|
117
|
+
activeOriginalWrite = undefined;
|
|
118
|
+
}
|
|
119
|
+
activeBridge = undefined;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
return bridge;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=RustLogBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RustLogBridge.js","sourceRoot":"","sources":["../src/RustLogBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface SpectrumContainer {
|
|
2
|
+
singleton(token: unknown, factory: () => unknown): void;
|
|
3
|
+
resolve<T = unknown>(token: unknown): T;
|
|
4
|
+
}
|
|
5
|
+
interface SpectrumConfigStore {
|
|
6
|
+
get<T = unknown>(key: string): T | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface SpectrumAppContext {
|
|
9
|
+
container: SpectrumContainer;
|
|
10
|
+
config: SpectrumConfigStore;
|
|
11
|
+
}
|
|
12
|
+
export default class SpectrumProvider {
|
|
13
|
+
protected app: SpectrumAppContext;
|
|
14
|
+
constructor(app: SpectrumAppContext);
|
|
15
|
+
register(): void;
|
|
16
|
+
boot(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=SpectrumProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpectrumProvider.d.ts","sourceRoot":"","sources":["../src/SpectrumProvider.ts"],"names":[],"mappings":"AAaA,UAAU,iBAAiB;IAC1B,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CACxC;AAED,UAAU,mBAAmB;IAC5B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,EAAE,mBAAmB,CAAC;CAC5B;AAED,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACxB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAkBF,IAAI;CAGV"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ConsoleChannel } from "./channels/ConsoleChannel.js";
|
|
2
|
+
import { Logger } from "./Logger.js";
|
|
3
|
+
import { setLogger } from "./services/main.js";
|
|
4
|
+
import { LOG_LEVEL_ORDER } from "./types.js";
|
|
5
|
+
const VALID_LEVELS = new Set(Object.keys(LOG_LEVEL_ORDER));
|
|
6
|
+
function resolveLogLevel(raw) {
|
|
7
|
+
if (raw && VALID_LEVELS.has(raw))
|
|
8
|
+
return raw;
|
|
9
|
+
return "info";
|
|
10
|
+
}
|
|
11
|
+
export default class SpectrumProvider {
|
|
12
|
+
app;
|
|
13
|
+
constructor(app) {
|
|
14
|
+
this.app = app;
|
|
15
|
+
}
|
|
16
|
+
register() {
|
|
17
|
+
this.app.container.singleton(Logger, () => {
|
|
18
|
+
const config = this.app.config.get("logger");
|
|
19
|
+
const level = config?.level && VALID_LEVELS.has(config.level)
|
|
20
|
+
? config.level
|
|
21
|
+
: resolveLogLevel(process.env.LOG_LEVEL);
|
|
22
|
+
return new Logger({
|
|
23
|
+
level,
|
|
24
|
+
channels: [new ConsoleChannel("pretty")],
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
this.app.container.singleton("logger", () => {
|
|
28
|
+
return this.app.container.resolve(Logger);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async boot() {
|
|
32
|
+
setLogger(this.app.container.resolve(Logger));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=SpectrumProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpectrumProvider.js","sourceRoot":"","sources":["../src/SpectrumProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;AAEnE,SAAS,eAAe,CAAC,GAAuB;IAC/C,IAAI,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,GAAe,CAAC;IACzD,OAAO,MAAM,CAAC;AACf,CAAC;AAgBD,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACd;IAAtB,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAuB,QAAQ,CAAC,CAAC;YACnE,MAAM,KAAK,GACV,MAAM,EAAE,KAAK,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC9C,CAAC,CAAC,MAAM,CAAC,KAAK;gBACd,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC3C,OAAO,IAAI,MAAM,CAAC;gBACjB,KAAK;gBACL,QAAQ,EAAE,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;aACxC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACT,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;CACD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console log channel — pretty-print in dev, JSON in prod.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR57, FR58
|
|
5
|
+
*/
|
|
6
|
+
import type { LogChannel, LogEntry } from "../types.js";
|
|
7
|
+
export declare class ConsoleChannel implements LogChannel {
|
|
8
|
+
#private;
|
|
9
|
+
name: string;
|
|
10
|
+
constructor(format?: "pretty" | "json");
|
|
11
|
+
write(entry: LogEntry): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ConsoleChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsoleChannel.d.ts","sourceRoot":"","sources":["../../src/channels/ConsoleChannel.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,qBAAa,cAAe,YAAW,UAAU;;IAChD,IAAI,SAAa;gBAGL,MAAM,GAAE,QAAQ,GAAG,MAAiB;IAIhD,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAmF5B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console log channel — pretty-print in dev, JSON in prod.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR57, FR58
|
|
5
|
+
*/
|
|
6
|
+
export class ConsoleChannel {
|
|
7
|
+
name = "console";
|
|
8
|
+
#format;
|
|
9
|
+
constructor(format = "pretty") {
|
|
10
|
+
this.#format = format;
|
|
11
|
+
}
|
|
12
|
+
write(entry) {
|
|
13
|
+
if (this.#format === "json") {
|
|
14
|
+
this.#writeJson(entry);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
this.#writePretty(entry);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
#writeJson(entry) {
|
|
21
|
+
// Data nested under 'data' key — no spread to prevent key collisions
|
|
22
|
+
const output = JSON.stringify({
|
|
23
|
+
timestamp: entry.timestamp,
|
|
24
|
+
level: entry.level,
|
|
25
|
+
module: entry.module,
|
|
26
|
+
message: entry.message,
|
|
27
|
+
correlationId: entry.correlationId,
|
|
28
|
+
data: entry.data,
|
|
29
|
+
});
|
|
30
|
+
this.#writeToStream(entry.level, `${output}\n`);
|
|
31
|
+
}
|
|
32
|
+
#sanitize(str) {
|
|
33
|
+
// Strip ANSI escape sequences. ESC (0x1B) is a control char so we match
|
|
34
|
+
// it via String.fromCharCode rather than a /\x1b/ regex (Biome's
|
|
35
|
+
// noControlCharactersInRegex rule rightly flags the literal form).
|
|
36
|
+
const ESC = String.fromCharCode(0x1b);
|
|
37
|
+
return str
|
|
38
|
+
.replace(/\r/g, "\\r")
|
|
39
|
+
.replace(/\n/g, "\\n")
|
|
40
|
+
.replaceAll(ESC, "[ESC]");
|
|
41
|
+
}
|
|
42
|
+
#writePretty(entry) {
|
|
43
|
+
const time = entry.timestamp.substring(11, 19); // HH:MM:SS
|
|
44
|
+
const levelStr = entry.level.toUpperCase().padEnd(5);
|
|
45
|
+
const prefix = this.#levelPrefix(entry.level);
|
|
46
|
+
// Sanitize every interpolated piece — `module` is usually
|
|
47
|
+
// developer-controlled but `correlationId` typically flows in from
|
|
48
|
+
// an HTTP header (X-Request-Id / X-Correlation-Id) and can carry
|
|
49
|
+
// attacker-supplied CRLF that would otherwise forge fake log lines.
|
|
50
|
+
const cidRaw = entry.correlationId
|
|
51
|
+
? entry.correlationId.length > 8
|
|
52
|
+
? `${entry.correlationId.substring(0, 8)}…`
|
|
53
|
+
: entry.correlationId
|
|
54
|
+
: "";
|
|
55
|
+
const cid = cidRaw ? ` cid=${this.#sanitize(cidRaw)}` : "";
|
|
56
|
+
const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` : "";
|
|
57
|
+
const message = this.#sanitize(entry.message);
|
|
58
|
+
const module = this.#sanitize(entry.module);
|
|
59
|
+
this.#writeToStream(entry.level, `${prefix} ${time} ${levelStr} [${module}] ${message}${cid}${dataStr}\n`);
|
|
60
|
+
}
|
|
61
|
+
/** Route error/fatal to stderr, others to stdout. */
|
|
62
|
+
#writeToStream(level, output) {
|
|
63
|
+
if (level === "error" || level === "fatal") {
|
|
64
|
+
process.stderr.write(output);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
process.stdout.write(output);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
#levelPrefix(level) {
|
|
71
|
+
switch (level) {
|
|
72
|
+
case "trace":
|
|
73
|
+
return " ";
|
|
74
|
+
case "debug":
|
|
75
|
+
return " ";
|
|
76
|
+
case "info":
|
|
77
|
+
return "i";
|
|
78
|
+
case "warn":
|
|
79
|
+
return "!";
|
|
80
|
+
case "error":
|
|
81
|
+
return "x";
|
|
82
|
+
case "fatal":
|
|
83
|
+
return "X";
|
|
84
|
+
default:
|
|
85
|
+
return " ";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=ConsoleChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsoleChannel.js","sourceRoot":"","sources":["../../src/channels/ConsoleChannel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File logging channel — writes log entries to a file as JSON lines.
|
|
3
|
+
*
|
|
4
|
+
* Uses a non-blocking WriteStream with an internal buffer. Rotation is
|
|
5
|
+
* scheduled via queueMicrotask to avoid blocking the event loop on the
|
|
6
|
+
* HTTP hot path.
|
|
7
|
+
*
|
|
8
|
+
* @implements MISS-14, MISS-15
|
|
9
|
+
*/
|
|
10
|
+
import type { LogChannel, LogEntry } from "../types.js";
|
|
11
|
+
export interface FileChannelConfig {
|
|
12
|
+
path: string;
|
|
13
|
+
maxSizeBytes?: number;
|
|
14
|
+
maxFiles?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class FileChannel implements LogChannel {
|
|
17
|
+
#private;
|
|
18
|
+
name: string;
|
|
19
|
+
constructor(config: FileChannelConfig);
|
|
20
|
+
write(entry: LogEntry): void;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=FileChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileChannel.d.ts","sourceRoot":"","sources":["../../src/channels/FileChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAY,YAAW,UAAU;;IAC7C,IAAI,SAAU;gBAUF,MAAM,EAAE,iBAAiB;IAMrC,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IA6H5B,KAAK,IAAI,IAAI;CAMb"}
|