@bjro/slog 1.0.2 → 1.0.4
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/package.json +1 -1
- package/src/hono.ts +11 -0
- package/src/index.ts +1 -0
- package/src/levels.ts +1 -0
- package/src/logger.ts +5 -0
- package/src/plugins/errorSerializer.ts +1 -0
- package/src/plugins/fieldEnrich.ts +5 -0
- package/src/plugins/levelFilter.ts +5 -0
- package/src/plugins/redact.ts +5 -0
- package/src/transports/console.ts +4 -0
- package/src/transports/http.ts +9 -0
- package/src/transports/pretty.ts +7 -0
- package/src/transports/routed.ts +23 -0
- package/src/types.ts +56 -0
package/package.json
CHANGED
package/src/hono.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @module slog/hono — Hono middleware for per-request structured logging. */
|
|
1
2
|
import { createMiddleware } from 'hono/factory';
|
|
2
3
|
import { getRuntimeKey } from 'hono/adapter';
|
|
3
4
|
import type { MiddlewareHandler } from 'hono';
|
|
@@ -5,6 +6,16 @@ import type { Logger } from './types.ts';
|
|
|
5
6
|
|
|
6
7
|
export type { Logger } from './types.ts';
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Creates Hono middleware that attaches a per-request logger to the context and logs request completion.
|
|
11
|
+
*
|
|
12
|
+
* The middleware enriches a child logger with request metadata (request ID, method, path, user-agent),
|
|
13
|
+
* stores it at `c.get('logger')`, and emits an `info` entry on every response. If the handler throws,
|
|
14
|
+
* an `error` entry is also emitted. On Cloudflare Workers the flush is handed to `waitUntil`.
|
|
15
|
+
*
|
|
16
|
+
* @param logger - A preconfigured Logger instance to derive per-request child loggers from.
|
|
17
|
+
* @returns A Hono MiddlewareHandler.
|
|
18
|
+
*/
|
|
8
19
|
export function slogMiddleware(logger: Logger): MiddlewareHandler {
|
|
9
20
|
return createMiddleware<{ Variables: { logger: Logger } }>(async (c, next) => {
|
|
10
21
|
const requestLogger = logger.withContext({
|
package/src/index.ts
CHANGED
package/src/levels.ts
CHANGED
package/src/logger.ts
CHANGED
|
@@ -90,6 +90,11 @@ class LoggerImpl implements Logger {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Creates a new structured logger instance.
|
|
95
|
+
* @param options - Logger configuration (level, plugins, transports, context).
|
|
96
|
+
* @returns A Logger instance.
|
|
97
|
+
*/
|
|
93
98
|
export function createLogger(options?: LoggerOptions): Logger {
|
|
94
99
|
return new LoggerImpl(options);
|
|
95
100
|
}
|
|
@@ -10,6 +10,7 @@ function serializeError(val: unknown): { name: string; message: string; stack?:
|
|
|
10
10
|
return { name: 'Error', message: String(val) };
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** Plugin that serializes Error instances in the `error` and `err` data fields into plain objects with name, message, and stack properties. */
|
|
13
14
|
export const errorSerializer: Plugin = {
|
|
14
15
|
name: 'errorSerializer',
|
|
15
16
|
transform(entry: LogEntry): LogEntry {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { LogEntry, Plugin } from '../types.ts';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Creates a plugin that merges static fields into every log entry's data.
|
|
5
|
+
* @param fields - Key-value pairs to add to each entry. Snapshot is taken at creation time.
|
|
6
|
+
* @returns A Plugin that enriches entries with the provided fields.
|
|
7
|
+
*/
|
|
3
8
|
export function createFieldEnrichPlugin(fields: Record<string, unknown>): Plugin {
|
|
4
9
|
const frozenFields = Object.freeze({ ...fields });
|
|
5
10
|
return {
|
|
@@ -2,6 +2,11 @@ import type { LogEntry, Plugin } from '../types.ts';
|
|
|
2
2
|
import type { LogLevel } from '../types.ts';
|
|
3
3
|
import { LOG_LEVELS } from '../levels.ts';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Creates a plugin that drops log entries below the specified minimum severity level.
|
|
7
|
+
* @param minLevel - Minimum log level to allow through.
|
|
8
|
+
* @returns A Plugin that filters entries below minLevel.
|
|
9
|
+
*/
|
|
5
10
|
export function createLevelFilterPlugin(minLevel: LogLevel): Plugin {
|
|
6
11
|
const threshold = LOG_LEVELS[minLevel];
|
|
7
12
|
return {
|
package/src/plugins/redact.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { LogEntry, Plugin } from '../types.ts';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Creates a plugin that replaces values of matching data keys with `[REDACTED]`.
|
|
5
|
+
* @param patterns - Array of exact key strings or RegExp patterns to match against data field keys.
|
|
6
|
+
* @returns A Plugin that redacts matched fields.
|
|
7
|
+
*/
|
|
3
8
|
export function createRedactPlugin(patterns: Array<string | RegExp>): Plugin {
|
|
4
9
|
return {
|
|
5
10
|
name: 'redact',
|
|
@@ -9,6 +9,10 @@ const CONSOLE_METHOD: Record<LogLevel, 'log' | 'info' | 'warn' | 'error'> = {
|
|
|
9
9
|
fatal: 'error',
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Creates a transport that writes JSON-serialized log entries to the console using level-appropriate methods.
|
|
14
|
+
* @returns A Transport that writes NDJSON to console.
|
|
15
|
+
*/
|
|
12
16
|
export function createConsoleTransport(): Transport {
|
|
13
17
|
return {
|
|
14
18
|
write(entry: LogEntry): void {
|
package/src/transports/http.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { LogEntry, Transport } from '../types.ts';
|
|
2
2
|
|
|
3
|
+
/** Configuration for the HTTP batch transport. */
|
|
3
4
|
export interface HttpBatchTransportConfig {
|
|
5
|
+
/** The URL to POST NDJSON batches to. */
|
|
4
6
|
url: string;
|
|
7
|
+
/** Optional HTTP headers to include in every POST request. */
|
|
5
8
|
headers?: Record<string, string>;
|
|
9
|
+
/** Interval in milliseconds between automatic flushes. When omitted, the transport only flushes on explicit flush() calls. */
|
|
6
10
|
flushInterval?: number; // ms, optional auto-flush timer
|
|
7
11
|
}
|
|
8
12
|
|
|
@@ -24,6 +28,11 @@ async function postWithRetry(url: string, body: string, headers: Record<string,
|
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Creates a transport that buffers entries in memory and sends them as NDJSON batches via HTTP POST on flush.
|
|
33
|
+
* @param config - HTTP batch transport configuration.
|
|
34
|
+
* @returns A Transport that batches and sends entries over HTTP.
|
|
35
|
+
*/
|
|
27
36
|
export function createHttpBatchTransport(config: HttpBatchTransportConfig): Transport {
|
|
28
37
|
const buffer: LogEntry[] = [];
|
|
29
38
|
let timer: ReturnType<typeof setInterval> | undefined;
|
package/src/transports/pretty.ts
CHANGED
|
@@ -20,7 +20,9 @@ const LEVEL_COLOR: Record<LogLevel, string> = {
|
|
|
20
20
|
|
|
21
21
|
const RESET = '\x1b[0m';
|
|
22
22
|
|
|
23
|
+
/** Configuration for the pretty-print transport. */
|
|
23
24
|
export interface PrettyTransportConfig {
|
|
25
|
+
/** Maximum number of characters to render for any single field value before truncating with `...`. Defaults to 120. */
|
|
24
26
|
maxValueLength?: number;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -46,6 +48,11 @@ function formatFields(obj: Record<string, unknown>, maxLen: number): string {
|
|
|
46
48
|
.join(' ');
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Creates a transport that writes colorized, human-readable log output to the console.
|
|
53
|
+
* @param config - Optional pretty-print configuration.
|
|
54
|
+
* @returns A Transport that writes human-readable output.
|
|
55
|
+
*/
|
|
49
56
|
export function createPrettyTransport(config?: PrettyTransportConfig): Transport {
|
|
50
57
|
const maxValueLength = config?.maxValueLength ?? 120;
|
|
51
58
|
|
package/src/transports/routed.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { LogEntry, LogLevel, Transport } from '../types.ts';
|
|
2
2
|
import { LOG_LEVELS } from '../levels.ts';
|
|
3
3
|
|
|
4
|
+
/** A predicate-transport pair for routing log entries. */
|
|
4
5
|
export interface TransportRoute {
|
|
6
|
+
/** Function that returns true if the entry should be sent to this route's transport. */
|
|
5
7
|
predicate: (entry: LogEntry) => boolean;
|
|
8
|
+
/** The transport to write matching entries to. */
|
|
6
9
|
transport: Transport;
|
|
7
10
|
}
|
|
8
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Creates a transport that routes each entry to transports whose predicates match.
|
|
14
|
+
* @param routes - Array of predicate-transport pairs.
|
|
15
|
+
* @returns A Transport that routes entries by predicate.
|
|
16
|
+
*/
|
|
9
17
|
export function createRoutedTransport(routes: TransportRoute[]): Transport {
|
|
10
18
|
return {
|
|
11
19
|
write(entry: LogEntry): void {
|
|
@@ -21,15 +29,30 @@ export function createRoutedTransport(routes: TransportRoute[]): Transport {
|
|
|
21
29
|
};
|
|
22
30
|
}
|
|
23
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Creates a predicate that matches entries at or above the given severity level.
|
|
34
|
+
* @param level - The minimum severity level to match.
|
|
35
|
+
* @returns A predicate function that returns true for entries at or above the given level.
|
|
36
|
+
*/
|
|
24
37
|
export function atOrAboveLevel(level: LogLevel): (entry: LogEntry) => boolean {
|
|
25
38
|
const min = LOG_LEVELS[level];
|
|
26
39
|
return (entry) => LOG_LEVELS[entry.level] >= min;
|
|
27
40
|
}
|
|
28
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Creates a predicate that matches entries at exactly the given severity level.
|
|
44
|
+
* @param level - The exact severity level to match.
|
|
45
|
+
* @returns A predicate function that returns true for entries at exactly the given level.
|
|
46
|
+
*/
|
|
29
47
|
export function exactLevel(level: LogLevel): (entry: LogEntry) => boolean {
|
|
30
48
|
return (entry) => entry.level === level;
|
|
31
49
|
}
|
|
32
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Creates a predicate that matches entries below the given severity level.
|
|
53
|
+
* @param level - The exclusive upper bound severity level.
|
|
54
|
+
* @returns A predicate function that returns true for entries below the given level.
|
|
55
|
+
*/
|
|
33
56
|
export function belowLevel(level: LogLevel): (entry: LogEntry) => boolean {
|
|
34
57
|
const threshold = LOG_LEVELS[level];
|
|
35
58
|
return (entry) => LOG_LEVELS[entry.level] < threshold;
|
package/src/types.ts
CHANGED
|
@@ -1,37 +1,93 @@
|
|
|
1
|
+
/** Severity levels from least to most severe. */
|
|
1
2
|
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
2
3
|
|
|
4
|
+
/** A structured log record produced by the logger. */
|
|
3
5
|
export interface LogEntry {
|
|
6
|
+
/** Severity level of the entry. */
|
|
4
7
|
level: LogLevel;
|
|
8
|
+
/** Unix millisecond timestamp via Date.now(). */
|
|
5
9
|
timestamp: number; // Unix ms via Date.now()
|
|
10
|
+
/** Optional human-readable message. */
|
|
6
11
|
message?: string;
|
|
12
|
+
/** Contextual fields inherited from the logger or withContext(). */
|
|
7
13
|
context: Record<string, unknown>;
|
|
14
|
+
/** Per-call structured data fields. */
|
|
8
15
|
data: Record<string, unknown>;
|
|
9
16
|
}
|
|
10
17
|
|
|
18
|
+
/** Transforms or filters log entries before they reach transports. */
|
|
11
19
|
export interface Plugin {
|
|
20
|
+
/** Unique identifier for the plugin. */
|
|
12
21
|
name: string;
|
|
22
|
+
/**
|
|
23
|
+
* Transforms the given log entry, or returns null to drop it.
|
|
24
|
+
* @param entry - The log entry to transform.
|
|
25
|
+
* @returns The transformed entry, or null to drop it from the pipeline.
|
|
26
|
+
*/
|
|
13
27
|
transform(entry: LogEntry): LogEntry | null;
|
|
14
28
|
}
|
|
15
29
|
|
|
30
|
+
/** Delivers log entries to an output destination. */
|
|
16
31
|
export interface Transport {
|
|
32
|
+
/**
|
|
33
|
+
* Writes a log entry to the transport's internal buffer or directly to output.
|
|
34
|
+
* @param entry - The log entry to write.
|
|
35
|
+
*/
|
|
17
36
|
write(entry: LogEntry): void;
|
|
37
|
+
/** Flushes buffered entries to the output destination. */
|
|
18
38
|
flush(): Promise<void>;
|
|
19
39
|
}
|
|
20
40
|
|
|
41
|
+
/** Structured logger supporting leveled methods, child contexts, and flush. */
|
|
21
42
|
export interface Logger {
|
|
43
|
+
/**
|
|
44
|
+
* Logs an entry at trace level.
|
|
45
|
+
* @param data - Structured data fields; use a `message` key for a human-readable message.
|
|
46
|
+
*/
|
|
22
47
|
trace(data: object): void;
|
|
48
|
+
/**
|
|
49
|
+
* Logs an entry at debug level.
|
|
50
|
+
* @param data - Structured data fields; use a `message` key for a human-readable message.
|
|
51
|
+
*/
|
|
23
52
|
debug(data: object): void;
|
|
53
|
+
/**
|
|
54
|
+
* Logs an entry at info level.
|
|
55
|
+
* @param data - Structured data fields; use a `message` key for a human-readable message.
|
|
56
|
+
*/
|
|
24
57
|
info(data: object): void;
|
|
58
|
+
/**
|
|
59
|
+
* Logs an entry at warn level.
|
|
60
|
+
* @param data - Structured data fields; use a `message` key for a human-readable message.
|
|
61
|
+
*/
|
|
25
62
|
warn(data: object): void;
|
|
63
|
+
/**
|
|
64
|
+
* Logs an entry at error level.
|
|
65
|
+
* @param data - Structured data fields; use a `message` key for a human-readable message.
|
|
66
|
+
*/
|
|
26
67
|
error(data: object): void;
|
|
68
|
+
/**
|
|
69
|
+
* Logs an entry at fatal level.
|
|
70
|
+
* @param data - Structured data fields; use a `message` key for a human-readable message.
|
|
71
|
+
*/
|
|
27
72
|
fatal(data: object): void;
|
|
73
|
+
/**
|
|
74
|
+
* Returns a new Logger with additional context fields merged in.
|
|
75
|
+
* @param context - Key-value pairs to add to every subsequent log entry.
|
|
76
|
+
* @returns A new Logger instance with the merged context.
|
|
77
|
+
*/
|
|
28
78
|
withContext(context: Record<string, unknown>): Logger;
|
|
79
|
+
/** Flushes all buffered entries through transports and awaits completion. */
|
|
29
80
|
flush(): Promise<void>;
|
|
30
81
|
}
|
|
31
82
|
|
|
83
|
+
/** Configuration for createLogger(). */
|
|
32
84
|
export interface LoggerOptions {
|
|
85
|
+
/** Minimum log level to emit. Entries below this level are silently dropped. Defaults to `'info'`. */
|
|
33
86
|
level?: LogLevel;
|
|
87
|
+
/** Array of plugins applied to each entry in order before transport delivery. */
|
|
34
88
|
plugins?: Plugin[];
|
|
89
|
+
/** Array of transports that receive entries on flush. */
|
|
35
90
|
transports?: Transport[];
|
|
91
|
+
/** Initial context fields merged into every log entry produced by this logger. */
|
|
36
92
|
context?: Record<string, unknown>;
|
|
37
93
|
}
|