@cleverbrush/log 0.0.0-beta-20260424142030
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 +80 -0
- package/dist/Enricher.d.ts +8 -0
- package/dist/Filter.d.ts +7 -0
- package/dist/LogContext.d.ts +58 -0
- package/dist/LogEvent.d.ts +24 -0
- package/dist/LogLevel.d.ts +44 -0
- package/dist/Logger.d.ts +111 -0
- package/dist/LoggerPipeline.d.ts +48 -0
- package/dist/MessageTemplate.d.ts +58 -0
- package/dist/SelfLog.d.ts +37 -0
- package/dist/Sink.d.ts +14 -0
- package/dist/chunk-EU6TDBKQ.js +3 -0
- package/dist/chunk-EU6TDBKQ.js.map +1 -0
- package/dist/clickhouse.d.ts +1 -0
- package/dist/clickhouse.js +16 -0
- package/dist/clickhouse.js.map +1 -0
- package/dist/correlation.d.ts +22 -0
- package/dist/createLogger.d.ts +51 -0
- package/dist/di.d.ts +29 -0
- package/dist/enrichers/application.d.ts +8 -0
- package/dist/enrichers/caller.d.ts +10 -0
- package/dist/enrichers/correlationId.d.ts +8 -0
- package/dist/enrichers/environment.d.ts +8 -0
- package/dist/enrichers/hostname.d.ts +8 -0
- package/dist/enrichers/index.d.ts +6 -0
- package/dist/enrichers/processId.d.ts +8 -0
- package/dist/formatters/ClefFormatter.d.ts +33 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/correlationId.d.ts +28 -0
- package/dist/middleware/requestLogging.d.ts +41 -0
- package/dist/samplingFilter.d.ts +23 -0
- package/dist/serialization.d.ts +25 -0
- package/dist/sinks/BatchingSink.d.ts +45 -0
- package/dist/sinks/ClickHouseSink.d.ts +78 -0
- package/dist/sinks/ConsoleSink.d.ts +29 -0
- package/dist/sinks/FileSink.d.ts +43 -0
- package/dist/sinks/SeqSink.d.ts +37 -0
- package/dist/sinks/createSink.d.ts +35 -0
- package/dist/sinks/index.d.ts +5 -0
- package/dist/useLogging.d.ts +35 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# @cleverbrush/log
|
|
2
|
+
|
|
3
|
+
Enterprise structured logging for TypeScript — Serilog-style message templates, CLEF format, batching sinks with circuit breaking, ambient correlation IDs.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @cleverbrush/log
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
createLogger,
|
|
16
|
+
consoleSink,
|
|
17
|
+
hostnameEnricher,
|
|
18
|
+
processIdEnricher,
|
|
19
|
+
} from '@cleverbrush/log';
|
|
20
|
+
|
|
21
|
+
const logger = createLogger({
|
|
22
|
+
minimumLevel: 'information',
|
|
23
|
+
sinks: [consoleSink({ theme: 'dark' })],
|
|
24
|
+
enrichers: [hostnameEnricher(), processIdEnricher()],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
logger.info('Server started on port {Port}', { Port: 3000 });
|
|
28
|
+
logger.error(new Error('oops'), 'Request failed for {UserId}', { UserId: 42 });
|
|
29
|
+
|
|
30
|
+
await logger.dispose();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **Message Templates** — `{Named}` properties captured as structured data
|
|
36
|
+
- **Typed Templates** — `TypedTemplate<T>` via `@cleverbrush/schema` for compile-time checked, groupable log events
|
|
37
|
+
- **CLEF Format** — Compact Log Event Format for Seq, ClickHouse, etc.
|
|
38
|
+
- **Sinks** — Console, File (with rotation), Seq, ClickHouse, custom
|
|
39
|
+
- **Batching** — All network sinks batch with retry & circuit breaking
|
|
40
|
+
- **Enrichers** — hostname, processId, environment, application, correlationId, caller
|
|
41
|
+
- **Correlation IDs** — UUID v7, extracted from headers, propagated via AsyncLocalStorage
|
|
42
|
+
- **Middleware** — Request logging & correlation ID for `@cleverbrush/server`
|
|
43
|
+
- **DI** — `configureLogging()` for `@cleverbrush/di`
|
|
44
|
+
- **Sampling** — Per-level sampling filters
|
|
45
|
+
|
|
46
|
+
## Typed Templates
|
|
47
|
+
|
|
48
|
+
Pass a `ParseStringSchemaBuilder` (from `@cleverbrush/schema`) directly to any log method. The logger uses the raw `{Property}` pattern as `messageTemplate` so all events of the same shape are grouped in Seq, ClickStack, ClickHouse, etc., while the rendered message is interpolated as usual.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { s } from '@cleverbrush/schema';
|
|
52
|
+
import { createLogger, consoleSink } from '@cleverbrush/log';
|
|
53
|
+
|
|
54
|
+
// Define once — compile-time checked parameter types
|
|
55
|
+
const TodoCreated = s.parseString('Todo #{TodoId} "{Title}" created by {UserId}');
|
|
56
|
+
|
|
57
|
+
const logger = createLogger({ sinks: [consoleSink()] });
|
|
58
|
+
|
|
59
|
+
// TypeScript enforces { TodoId, Title, UserId }
|
|
60
|
+
logger.info(TodoCreated, { TodoId: 1, Title: 'Buy milk', UserId: 'u-42' });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Correlation Middleware
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { useLogging } from '@cleverbrush/log';
|
|
67
|
+
|
|
68
|
+
// Returns [correlationIdMiddleware, requestLoggingMiddleware]
|
|
69
|
+
const [correlationId, requestLogging] = useLogging(logger, {
|
|
70
|
+
excludePaths: ['/health'],
|
|
71
|
+
// Set to false when OTel traceparent already provides traceability
|
|
72
|
+
correlationResponseHeader: false,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`correlationResponseHeader: false` suppresses the `X-Correlation-Id` response header entirely — useful when `@cleverbrush/otel`'s tracing middleware already sets a `traceparent` / `traceresponse` header and a second ID would be redundant.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
BSD-3-Clause
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LogEvent } from './LogEvent.js';
|
|
2
|
+
/**
|
|
3
|
+
* An enricher adds or transforms properties on a log event.
|
|
4
|
+
*
|
|
5
|
+
* Enrichers are pure functions that return a new event with additional
|
|
6
|
+
* properties — they must not mutate the input event.
|
|
7
|
+
*/
|
|
8
|
+
export type Enricher = (event: LogEvent) => LogEvent;
|
package/dist/Filter.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Logger } from './Logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Store shape for the ambient log context.
|
|
4
|
+
*/
|
|
5
|
+
export interface LogContextStore {
|
|
6
|
+
logger: Logger;
|
|
7
|
+
correlationId?: string;
|
|
8
|
+
properties?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Ambient logger context using `AsyncLocalStorage`.
|
|
12
|
+
*
|
|
13
|
+
* Zero overhead when not used — the `AsyncLocalStorage` instance
|
|
14
|
+
* is only created once, and `getStore()` is a near-zero-cost operation.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* LogContext.run(logger, async () => {
|
|
19
|
+
* const log = LogContext.current()!;
|
|
20
|
+
* log.info('Inside context');
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const LogContext: {
|
|
25
|
+
/**
|
|
26
|
+
* Runs a callback with the given logger as the ambient context.
|
|
27
|
+
*
|
|
28
|
+
* @param logger - the logger to set as ambient
|
|
29
|
+
* @param fn - the async function to run within the context
|
|
30
|
+
* @returns the result of the callback
|
|
31
|
+
*/
|
|
32
|
+
run<T>(logger: Logger, fn: () => T): T;
|
|
33
|
+
/**
|
|
34
|
+
* Returns the ambient logger, or `undefined` if no context is active.
|
|
35
|
+
*/
|
|
36
|
+
current(): Logger | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Returns the raw store, useful for enrichers to read correlation IDs.
|
|
39
|
+
*/
|
|
40
|
+
getStore(): LogContextStore | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Runs a callback with additional enrichment properties added
|
|
43
|
+
* to the ambient logger context.
|
|
44
|
+
*
|
|
45
|
+
* @param properties - additional properties to add to the context logger
|
|
46
|
+
* @param fn - the async function to run
|
|
47
|
+
* @returns the result of the callback
|
|
48
|
+
*/
|
|
49
|
+
enrichWith<T>(properties: Record<string, unknown>, fn: () => T): T;
|
|
50
|
+
/**
|
|
51
|
+
* Runs a callback with a correlation ID set in the ambient context.
|
|
52
|
+
*
|
|
53
|
+
* @param correlationId - the correlation ID to set
|
|
54
|
+
* @param fn - the async function to run
|
|
55
|
+
* @returns the result of the callback
|
|
56
|
+
*/
|
|
57
|
+
runWithCorrelationId<T>(correlationId: string, fn: () => T): T;
|
|
58
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LogLevel } from './LogLevel.js';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a single structured log event in the pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Log events carry both a human-readable rendered message and the original
|
|
6
|
+
* message template with structured property values, enabling both
|
|
7
|
+
* human-friendly display and machine-queryable structured search.
|
|
8
|
+
*/
|
|
9
|
+
export interface LogEvent {
|
|
10
|
+
/** ISO timestamp when the event occurred. */
|
|
11
|
+
timestamp: Date;
|
|
12
|
+
/** Severity level of the event. */
|
|
13
|
+
level: LogLevel;
|
|
14
|
+
/** The raw message template with `{Property}` holes. */
|
|
15
|
+
messageTemplate: string;
|
|
16
|
+
/** The fully interpolated, human-readable message. */
|
|
17
|
+
renderedMessage: string;
|
|
18
|
+
/** Structured properties extracted from the template and enrichers. */
|
|
19
|
+
properties: Record<string, unknown>;
|
|
20
|
+
/** The exception associated with this event, if any. */
|
|
21
|
+
exception?: Error;
|
|
22
|
+
/** Deterministic hex hash of `messageTemplate` — maps to CLEF `@i`. */
|
|
23
|
+
eventId?: string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log severity levels as a const object.
|
|
3
|
+
*
|
|
4
|
+
* Use as values (`LogLevel.Information`) or as a type (`LogLevel`).
|
|
5
|
+
* Levels are ordered numerically — higher values indicate greater severity.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* if (logger.isEnabled(LogLevel.Debug)) {
|
|
10
|
+
* logger.debug('Expensive computation: {@Result}', { Result: compute() });
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare const LogLevel: {
|
|
15
|
+
readonly Trace: 0;
|
|
16
|
+
readonly Debug: 1;
|
|
17
|
+
readonly Information: 2;
|
|
18
|
+
readonly Warning: 3;
|
|
19
|
+
readonly Error: 4;
|
|
20
|
+
readonly Fatal: 5;
|
|
21
|
+
};
|
|
22
|
+
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
|
|
23
|
+
/**
|
|
24
|
+
* Log level name strings for configuration and display.
|
|
25
|
+
*/
|
|
26
|
+
export type LogLevelName = 'trace' | 'debug' | 'information' | 'warning' | 'error' | 'fatal';
|
|
27
|
+
/**
|
|
28
|
+
* Parses a log level name string to a `LogLevel` numeric value.
|
|
29
|
+
*
|
|
30
|
+
* @param name - case-insensitive level name
|
|
31
|
+
* @returns the numeric `LogLevel` value
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseLogLevel(name: string): LogLevel;
|
|
34
|
+
/**
|
|
35
|
+
* Converts a `LogLevel` numeric value to its string name.
|
|
36
|
+
*
|
|
37
|
+
* @param level - the numeric log level
|
|
38
|
+
* @returns the level name string
|
|
39
|
+
*/
|
|
40
|
+
export declare function levelToString(level: LogLevel): LogLevelName;
|
|
41
|
+
/**
|
|
42
|
+
* Three-letter abbreviation for display in console output.
|
|
43
|
+
*/
|
|
44
|
+
export declare function levelToShortString(level: LogLevel): string;
|
package/dist/Logger.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { LoggerPipeline } from './LoggerPipeline.js';
|
|
2
|
+
import { LogLevel, type LogLevelName } from './LogLevel.js';
|
|
3
|
+
/**
|
|
4
|
+
* A typed message template created via `ParseStringSchemaBuilder`.
|
|
5
|
+
*
|
|
6
|
+
* When passed to a `Logger` log method, the logger uses `template` as the
|
|
7
|
+
* `messageTemplate` (so events with the same shape are grouped in Seq /
|
|
8
|
+
* ClickStack / ClickHouse) and derives the rendered message by interpolating
|
|
9
|
+
* `template` with the supplied parameters.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { s } from '@cleverbrush/schema';
|
|
14
|
+
*
|
|
15
|
+
* // Build a reusable typed template once
|
|
16
|
+
* const tmpl = s.parseString('Todo #{TodoId} "{Title}" created by {UserId}');
|
|
17
|
+
*
|
|
18
|
+
* // All log sites share the same messageTemplate → groupable in the UI
|
|
19
|
+
* logger.info(tmpl, { TodoId: 1, Title: 'Buy milk', UserId: 'u-42' });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface TypedTemplate<T extends Record<string, unknown>> {
|
|
23
|
+
serialize(params: T): string;
|
|
24
|
+
/** The raw `{Property}` pattern string, used as `messageTemplate`. */
|
|
25
|
+
readonly template?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Structured logger with per-level methods, child context support,
|
|
29
|
+
* and `AsyncDisposable` for graceful shutdown.
|
|
30
|
+
*
|
|
31
|
+
* Log methods are synchronous and fire-and-forget — they push events
|
|
32
|
+
* into an internal async microtask pipeline.
|
|
33
|
+
*
|
|
34
|
+
* Accepts both plain string templates and typed {@link TypedTemplate}
|
|
35
|
+
* objects (produced by `ParseStringSchemaBuilder` from `@cleverbrush/schema`).
|
|
36
|
+
* Typed templates carry a `template` property with the raw `{Property}` pattern,
|
|
37
|
+
* which the logger uses as `messageTemplate` so all events of the same shape
|
|
38
|
+
* are grouped correctly in Seq, ClickStack, ClickHouse, etc.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* logger.info('Server started on port {Port}', { Port: 3000 });
|
|
43
|
+
*
|
|
44
|
+
* const child = logger.forContext('SourceContext', 'OrderService');
|
|
45
|
+
* child.info('Processing order {OrderId}', { OrderId: 42 });
|
|
46
|
+
*
|
|
47
|
+
* // Typed template — structured grouping
|
|
48
|
+
* import { s } from '@cleverbrush/schema';
|
|
49
|
+
* const tmpl = s.parseString('Order #{OrderId} placed by {UserId}');
|
|
50
|
+
* child.info(tmpl, { OrderId: 1, UserId: 'u-99' });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class Logger implements AsyncDisposable {
|
|
54
|
+
#private;
|
|
55
|
+
constructor(pipeline: LoggerPipeline, contextProperties?: Record<string, unknown>);
|
|
56
|
+
/**
|
|
57
|
+
* Checks whether the given level is enabled for this logger.
|
|
58
|
+
*
|
|
59
|
+
* @param level - the log level to check
|
|
60
|
+
* @returns `true` if events at this level would be processed
|
|
61
|
+
*/
|
|
62
|
+
isEnabled(level: LogLevel): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Creates a child logger with additional context properties.
|
|
65
|
+
*
|
|
66
|
+
* @param key - property name, or an object of key-value pairs
|
|
67
|
+
* @param value - property value (when key is a string)
|
|
68
|
+
* @returns a new `Logger` with merged context properties
|
|
69
|
+
*/
|
|
70
|
+
forContext(key: string | Record<string, unknown>, value?: unknown): Logger;
|
|
71
|
+
/**
|
|
72
|
+
* Changes the minimum log level at runtime.
|
|
73
|
+
*
|
|
74
|
+
* @param level - new minimum level (name string or numeric value)
|
|
75
|
+
*/
|
|
76
|
+
setMinimumLevel(level: LogLevelName | LogLevel): void;
|
|
77
|
+
/**
|
|
78
|
+
* Polls an environment variable for log level changes.
|
|
79
|
+
*
|
|
80
|
+
* @param envVar - environment variable name to watch
|
|
81
|
+
* @param intervalMs - polling interval in milliseconds (default: 30000)
|
|
82
|
+
*/
|
|
83
|
+
watchLevel(envVar: string, intervalMs?: number): void;
|
|
84
|
+
/** Log a trace-level message. */
|
|
85
|
+
trace(template: string, properties?: Record<string, unknown>): void;
|
|
86
|
+
trace<T extends Record<string, unknown>>(template: TypedTemplate<T>, properties: T): void;
|
|
87
|
+
/** Log a debug-level message. */
|
|
88
|
+
debug(template: string, properties?: Record<string, unknown>): void;
|
|
89
|
+
debug<T extends Record<string, unknown>>(template: TypedTemplate<T>, properties: T): void;
|
|
90
|
+
/** Log an information-level message. */
|
|
91
|
+
info(template: string, properties?: Record<string, unknown>): void;
|
|
92
|
+
info<T extends Record<string, unknown>>(template: TypedTemplate<T>, properties: T): void;
|
|
93
|
+
/** Log a warning-level message. */
|
|
94
|
+
warn(template: string, properties?: Record<string, unknown>): void;
|
|
95
|
+
warn<T extends Record<string, unknown>>(template: TypedTemplate<T>, properties: T): void;
|
|
96
|
+
/** Log an error-level message with an optional exception. */
|
|
97
|
+
error(template: string, properties?: Record<string, unknown>): void;
|
|
98
|
+
error(error: Error, template: string, properties?: Record<string, unknown>): void;
|
|
99
|
+
error<T extends Record<string, unknown>>(template: TypedTemplate<T>, properties: T): void;
|
|
100
|
+
error<T extends Record<string, unknown>>(error: Error, template: TypedTemplate<T>, properties: T): void;
|
|
101
|
+
/** Log a fatal-level message with an optional exception. */
|
|
102
|
+
fatal(template: string, properties?: Record<string, unknown>): void;
|
|
103
|
+
fatal(error: Error, template: string, properties?: Record<string, unknown>): void;
|
|
104
|
+
fatal<T extends Record<string, unknown>>(template: TypedTemplate<T>, properties: T): void;
|
|
105
|
+
fatal<T extends Record<string, unknown>>(error: Error, template: TypedTemplate<T>, properties: T): void;
|
|
106
|
+
/** Flushes all pending events through the pipeline to sinks. */
|
|
107
|
+
flush(): Promise<void>;
|
|
108
|
+
/** Flushes all sinks and releases resources. */
|
|
109
|
+
dispose(): Promise<void>;
|
|
110
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
111
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Enricher } from './Enricher.js';
|
|
2
|
+
import type { LogFilter } from './Filter.js';
|
|
3
|
+
import type { LogEvent } from './LogEvent.js';
|
|
4
|
+
import { type LogLevel, type LogLevelName } from './LogLevel.js';
|
|
5
|
+
import type { LogSink } from './Sink.js';
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the logger pipeline.
|
|
8
|
+
*/
|
|
9
|
+
export interface PipelineConfig {
|
|
10
|
+
minimumLevel: LogLevel;
|
|
11
|
+
levelOverrides?: Record<string, LogLevelName>;
|
|
12
|
+
sinks: LogSink[];
|
|
13
|
+
enrichers?: Enricher[];
|
|
14
|
+
filters?: LogFilter[];
|
|
15
|
+
maxQueueSize?: number;
|
|
16
|
+
dropPolicy?: 'dropOldest' | 'dropNewest' | 'block';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Internal pipeline that processes log events asynchronously.
|
|
20
|
+
*
|
|
21
|
+
* Events are pushed into a queue and processed via microtask. The
|
|
22
|
+
* pipeline applies enrichers, filters, and level overrides before
|
|
23
|
+
* fanning out to all configured sinks.
|
|
24
|
+
*/
|
|
25
|
+
export declare class LoggerPipeline {
|
|
26
|
+
#private;
|
|
27
|
+
constructor(config: PipelineConfig);
|
|
28
|
+
get minimumLevel(): LogLevel;
|
|
29
|
+
set minimumLevel(level: LogLevel);
|
|
30
|
+
/**
|
|
31
|
+
* Checks if the given level would pass the minimum level check
|
|
32
|
+
* for the given source context.
|
|
33
|
+
*/
|
|
34
|
+
isEnabled(level: LogLevel, sourceContext?: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Enqueues a log event for processing.
|
|
37
|
+
* Fire-and-forget — callers never await.
|
|
38
|
+
*/
|
|
39
|
+
push(event: LogEvent): void;
|
|
40
|
+
/**
|
|
41
|
+
* Forces all queued events through the pipeline and into sinks.
|
|
42
|
+
*/
|
|
43
|
+
flush(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Flushes remaining events and disposes all sinks.
|
|
46
|
+
*/
|
|
47
|
+
dispose(): Promise<void>;
|
|
48
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { LogEvent } from './LogEvent.js';
|
|
2
|
+
import type { LogLevel } from './LogLevel.js';
|
|
3
|
+
interface TemplateToken {
|
|
4
|
+
type: 'text' | 'property';
|
|
5
|
+
value: string;
|
|
6
|
+
destructure?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Parses a Serilog-style message template into tokens.
|
|
10
|
+
*
|
|
11
|
+
* Supports `{Property}` for scalar capture and `{@Property}` for
|
|
12
|
+
* destructuring (full object structure preserved in properties).
|
|
13
|
+
*
|
|
14
|
+
* @param template - message template string with `{Property}` holes
|
|
15
|
+
* @returns array of parsed tokens
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseTemplate(template: string): TemplateToken[];
|
|
18
|
+
/**
|
|
19
|
+
* Renders a parsed template into a human-readable string.
|
|
20
|
+
*
|
|
21
|
+
* For non-destructured properties, calls `toString()` if available on
|
|
22
|
+
* the value. For destructured properties (`{@Prop}`), uses JSON.stringify.
|
|
23
|
+
*
|
|
24
|
+
* @param tokens - parsed template tokens
|
|
25
|
+
* @param properties - property values to interpolate
|
|
26
|
+
* @returns the rendered message string
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderTemplate(tokens: TemplateToken[], properties: Record<string, unknown>): string;
|
|
29
|
+
/**
|
|
30
|
+
* Captures structured properties from the template, applying destructure
|
|
31
|
+
* semantics: `{@Prop}` keeps the full object, `{Prop}` calls `toString()`
|
|
32
|
+
* on objects that have a custom `toString`.
|
|
33
|
+
*
|
|
34
|
+
* @param tokens - parsed template tokens
|
|
35
|
+
* @param properties - raw property values
|
|
36
|
+
* @returns property bag with appropriate serialization applied
|
|
37
|
+
*/
|
|
38
|
+
export declare function captureProperties(tokens: TemplateToken[], properties: Record<string, unknown>): Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Generates a deterministic hex event ID from a message template string.
|
|
41
|
+
* Uses a simple FNV-1a hash for speed — no cryptographic requirements.
|
|
42
|
+
*
|
|
43
|
+
* @param template - the raw message template
|
|
44
|
+
* @returns 8-character hex hash
|
|
45
|
+
*/
|
|
46
|
+
export declare function computeEventId(template: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a complete `LogEvent` from a message template, properties,
|
|
49
|
+
* and metadata.
|
|
50
|
+
*
|
|
51
|
+
* @param level - severity level
|
|
52
|
+
* @param template - message template string with `{Property}` holes
|
|
53
|
+
* @param properties - structured property values
|
|
54
|
+
* @param exception - optional associated error
|
|
55
|
+
* @returns a fully populated `LogEvent`
|
|
56
|
+
*/
|
|
57
|
+
export declare function createLogEvent(level: LogLevel, template: string, properties: Record<string, unknown>, exception?: Error): LogEvent;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal diagnostic channel for the logging library's own errors.
|
|
3
|
+
*
|
|
4
|
+
* Sinks use this when they fail, instead of throwing and crashing
|
|
5
|
+
* the application. By default writes to `process.stderr`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* SelfLog.setOutput(fs.createWriteStream('./logs/selflog.txt'));
|
|
10
|
+
* SelfLog.disable();
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare const SelfLog: {
|
|
14
|
+
_enabled: boolean;
|
|
15
|
+
_output: null | {
|
|
16
|
+
write(s: string): void;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Writes an internal diagnostic message. Includes optional error details.
|
|
20
|
+
*
|
|
21
|
+
* @param message - diagnostic message
|
|
22
|
+
* @param error - optional associated error
|
|
23
|
+
*/
|
|
24
|
+
write(message: string, error?: unknown): void;
|
|
25
|
+
/**
|
|
26
|
+
* Sets a custom output stream for self-diagnostics.
|
|
27
|
+
*
|
|
28
|
+
* @param output - writable stream with a `write` method
|
|
29
|
+
*/
|
|
30
|
+
setOutput(output: {
|
|
31
|
+
write(s: string): void;
|
|
32
|
+
}): void;
|
|
33
|
+
/** Suppresses all internal diagnostic output. */
|
|
34
|
+
disable(): void;
|
|
35
|
+
/** Re-enables internal diagnostic output. */
|
|
36
|
+
enable(): void;
|
|
37
|
+
};
|
package/dist/Sink.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LogEvent } from './LogEvent.js';
|
|
2
|
+
/**
|
|
3
|
+
* A sink that receives batches of log events for output.
|
|
4
|
+
*
|
|
5
|
+
* Sinks must implement `AsyncDisposable` for graceful shutdown.
|
|
6
|
+
* The `flush()` method is optional and forces immediate delivery
|
|
7
|
+
* of any buffered events.
|
|
8
|
+
*/
|
|
9
|
+
export interface LogSink extends AsyncDisposable {
|
|
10
|
+
/** Write a batch of events to the output target. */
|
|
11
|
+
emit(events: LogEvent[]): Promise<void>;
|
|
12
|
+
/** Force immediate delivery of buffered events. */
|
|
13
|
+
flush?(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var r={Trace:0,Debug:1,Information:2,Warning:3,Error:4,Fatal:5},m={trace:r.Trace,debug:r.Debug,information:r.Information,warning:r.Warning,error:r.Error,fatal:r.Fatal},p={[r.Trace]:"trace",[r.Debug]:"debug",[r.Information]:"information",[r.Warning]:"warning",[r.Error]:"error",[r.Fatal]:"fatal"};function L(n){let t=n.toLowerCase(),a=m[t];if(a===void 0)throw new Error(`Invalid log level: "${n}". Valid levels: ${Object.keys(m).join(", ")}`);return a}function S(n){return p[n]??"information"}function k(n){switch(n){case r.Trace:return"TRC";case r.Debug:return"DBG";case r.Information:return"INF";case r.Warning:return"WRN";case r.Error:return"ERR";case r.Fatal:return"FTL";default:return"INF"}}function v(n,t){let a=t?.maxDepth??10,f=t?.maxStringLength??32768,s=new Set;return o(n,0);function o(e,u){if(e==null)return e;switch(typeof e){case"string":return e.length>f?e.slice(0,f)+"...(truncated)":e;case"number":case"boolean":return e;case"bigint":return e.toString();case"symbol":return`[Symbol: ${e.description??""}]`;case"function":return`[Function: ${e.name||"anonymous"}]`;case"object":return h(e,u);default:return String(e)}}function h(e,u){if(typeof Buffer<"u"&&Buffer.isBuffer(e))return`[Buffer(${e.length} bytes)]`;if(e instanceof Error){let l={name:e.name,message:e.message,stack:e.stack};for(let i of Object.getOwnPropertyNames(e))i!=="name"&&i!=="message"&&i!=="stack"&&(l[i]=o(e[i],u+1));return l}if(s.has(e))return"[Circular]";if(u>=a)return Array.isArray(e)?"[Array]":"[Object]";s.add(e);try{if(Array.isArray(e))return e.map(i=>o(i,u+1));if(e instanceof Date)return e.toISOString();if(e instanceof RegExp)return e.toString();if(e instanceof Map){let i={};for(let[d,y]of e)i[String(d)]=o(y,u+1);return i}if(e instanceof Set)return[...e].map(i=>o(i,u+1));let l={};for(let i of Object.keys(e))l[i]=o(e[i],u+1);return l}finally{s.delete(e)}}}var c={_enabled:!0,_output:null,write(n,t){if(!this._enabled)return;let a=new Date().toISOString(),f=t instanceof Error?` ${t.stack??t.message}`:t!==void 0?` ${String(t)}`:"",s=`[${a} SelfLog] ${n}${f}
|
|
2
|
+
`;this._output?this._output.write(s):typeof process<"u"&&process.stderr&&process.stderr.write(s)},setOutput(n){this._output=n},disable(){this._enabled=!1},enable(){this._enabled=!0}};var g=class{#a;#o;#c;#f;#i;#l;#h;#e=[];#t;#n=0;#s=!1;#r;#m=!1;constructor(t){this.#a=t.emit,this.#o=t.batchSize??100,this.#c=t.flushInterval??2e3,this.#f=t.maxQueueSize??5e4,this.#i=t.maxRetries??5,this.#l=t.retryDelay??1e3,this.#h=t.circuitBreakerThreshold??3}async emit(t){if(!this.#m){for(let a of t)this.#e.length>=this.#f&&this.#e.shift(),this.#e.push(a);this.#e.length>=this.#o?await this.flush():this.#g()}}async flush(){for(this.#u();this.#e.length>0;){if(this.#s){c.write("BatchingSink circuit breaker open \u2014 deferring flush");return}let t=this.#e.splice(0,this.#o),a,f=!1;for(let s=0;s<=this.#i;s++)try{await this.#a(t),this.#n=0,f=!0;break}catch(o){if(a=o,s<this.#i){let h=this.#l*2**s;await new Promise(e=>setTimeout(e,h))}}if(!f){this.#n++,c.write(`BatchingSink flush failed after ${this.#i+1} attempts`,a),this.#n>=this.#h&&(this.#s=!0,c.write("BatchingSink circuit breaker opened \u2014 backing off 30s"),this.#r=setTimeout(()=>{this.#s=!1,this.#n=0,c.write("BatchingSink circuit breaker closed"),this.#e.length>0&&this.flush().catch(s=>{c.write("BatchingSink post-circuit flush failed",s)})},3e4),this.#r.unref&&this.#r.unref());return}}}async[Symbol.asyncDispose](){if(this.#m=!0,this.#u(),this.#r&&clearTimeout(this.#r),this.#e.length>0&&!this.#s)try{await this.#a(this.#e.splice(0,this.#e.length))}catch(t){c.write("BatchingSink dispose flush failed",t)}}#g(){this.#u(),this.#t=setTimeout(()=>{this.flush().catch(t=>{c.write("BatchingSink timer flush failed",t)})},this.#c),this.#t.unref&&this.#t.unref()}#u(){this.#t&&(clearTimeout(this.#t),this.#t=void 0)}};export{r as a,L as b,S as c,k as d,v as e,c as f,g};
|
|
3
|
+
//# sourceMappingURL=chunk-EU6TDBKQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/LogLevel.ts","../src/serialization.ts","../src/SelfLog.ts","../src/sinks/BatchingSink.ts"],"sourcesContent":["/**\n * Log severity levels as a const object.\n *\n * Use as values (`LogLevel.Information`) or as a type (`LogLevel`).\n * Levels are ordered numerically — higher values indicate greater severity.\n *\n * @example\n * ```ts\n * if (logger.isEnabled(LogLevel.Debug)) {\n * logger.debug('Expensive computation: {@Result}', { Result: compute() });\n * }\n * ```\n */\nexport const LogLevel = {\n Trace: 0,\n Debug: 1,\n Information: 2,\n Warning: 3,\n Error: 4,\n Fatal: 5\n} as const;\n\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\n\n/**\n * Log level name strings for configuration and display.\n */\nexport type LogLevelName =\n | 'trace'\n | 'debug'\n | 'information'\n | 'warning'\n | 'error'\n | 'fatal';\n\nconst levelNameMap: Record<LogLevelName, LogLevel> = {\n trace: LogLevel.Trace,\n debug: LogLevel.Debug,\n information: LogLevel.Information,\n warning: LogLevel.Warning,\n error: LogLevel.Error,\n fatal: LogLevel.Fatal\n};\n\nconst levelStringMap: Record<LogLevel, LogLevelName> = {\n [LogLevel.Trace]: 'trace',\n [LogLevel.Debug]: 'debug',\n [LogLevel.Information]: 'information',\n [LogLevel.Warning]: 'warning',\n [LogLevel.Error]: 'error',\n [LogLevel.Fatal]: 'fatal'\n};\n\n/**\n * Parses a log level name string to a `LogLevel` numeric value.\n *\n * @param name - case-insensitive level name\n * @returns the numeric `LogLevel` value\n */\nexport function parseLogLevel(name: string): LogLevel {\n const normalized = name.toLowerCase() as LogLevelName;\n const level = levelNameMap[normalized];\n if (level === undefined) {\n throw new Error(\n `Invalid log level: \"${name}\". Valid levels: ${Object.keys(levelNameMap).join(', ')}`\n );\n }\n return level;\n}\n\n/**\n * Converts a `LogLevel` numeric value to its string name.\n *\n * @param level - the numeric log level\n * @returns the level name string\n */\nexport function levelToString(level: LogLevel): LogLevelName {\n return levelStringMap[level] ?? 'information';\n}\n\n/**\n * Three-letter abbreviation for display in console output.\n */\nexport function levelToShortString(level: LogLevel): string {\n switch (level) {\n case LogLevel.Trace:\n return 'TRC';\n case LogLevel.Debug:\n return 'DBG';\n case LogLevel.Information:\n return 'INF';\n case LogLevel.Warning:\n return 'WRN';\n case LogLevel.Error:\n return 'ERR';\n case LogLevel.Fatal:\n return 'FTL';\n default:\n return 'INF';\n }\n}\n","/**\n * Options for the safe serializer.\n */\nexport interface SerializationOptions {\n /** Maximum nesting depth for objects/arrays. @default 10 */\n maxDepth?: number;\n /** Maximum length for string values before truncation. @default 32768 */\n maxStringLength?: number;\n}\n\nconst DEFAULT_MAX_DEPTH = 10;\nconst DEFAULT_MAX_STRING_LENGTH = 32_768;\n\n/**\n * Safely serializes a value for structured logging, handling:\n * - Circular references → `\"[Circular]\"`\n * - Depth limits → `\"[Object]\"` or `\"[Array]\"`\n * - BigInt → string representation\n * - Buffer → `\"[Buffer(N bytes)]\"`\n * - Functions → `\"[Function: name]\"`\n * - Symbols → `\"[Symbol: description]\"`\n * - Error objects → `{ message, stack, name, ...ownProperties }`\n * - Long strings → truncated with `\"...(truncated)\"`\n *\n * @param value - the value to serialize\n * @param options - serialization limits\n * @returns a JSON-safe representation of the value\n */\nexport function safeSerialize(\n value: unknown,\n options?: SerializationOptions\n): unknown {\n const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;\n const maxStringLength =\n options?.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;\n const seen = new Set<object>();\n\n return serialize(value, 0);\n\n function serialize(val: unknown, depth: number): unknown {\n if (val === null || val === undefined) {\n return val;\n }\n\n switch (typeof val) {\n case 'string':\n if (val.length > maxStringLength) {\n return val.slice(0, maxStringLength) + '...(truncated)';\n }\n return val;\n\n case 'number':\n case 'boolean':\n return val;\n\n case 'bigint':\n return val.toString();\n\n case 'symbol':\n return `[Symbol: ${val.description ?? ''}]`;\n\n case 'function':\n return `[Function: ${val.name || 'anonymous'}]`;\n\n case 'object':\n return serializeObject(val as object, depth);\n\n default:\n return String(val);\n }\n }\n\n function serializeObject(obj: object, depth: number): unknown {\n // Buffer check\n if (typeof Buffer !== 'undefined' && Buffer.isBuffer(obj)) {\n return `[Buffer(${obj.length} bytes)]`;\n }\n\n // Error objects — extract structured data\n if (obj instanceof Error) {\n const result: Record<string, unknown> = {\n name: obj.name,\n message: obj.message,\n stack: obj.stack\n };\n for (const key of Object.getOwnPropertyNames(obj)) {\n if (key !== 'name' && key !== 'message' && key !== 'stack') {\n result[key] = serialize((obj as any)[key], depth + 1);\n }\n }\n return result;\n }\n\n // Circular reference check\n if (seen.has(obj)) {\n return '[Circular]';\n }\n\n // Depth limit\n if (depth >= maxDepth) {\n return Array.isArray(obj) ? '[Array]' : '[Object]';\n }\n\n seen.add(obj);\n\n try {\n if (Array.isArray(obj)) {\n return obj.map(item => serialize(item, depth + 1));\n }\n\n // Date objects\n if (obj instanceof Date) {\n return obj.toISOString();\n }\n\n // RegExp\n if (obj instanceof RegExp) {\n return obj.toString();\n }\n\n // Map\n if (obj instanceof Map) {\n const result: Record<string, unknown> = {};\n for (const [key, val] of obj) {\n result[String(key)] = serialize(val, depth + 1);\n }\n return result;\n }\n\n // Set\n if (obj instanceof Set) {\n return [...obj].map(item => serialize(item, depth + 1));\n }\n\n // Plain objects\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = serialize((obj as any)[key], depth + 1);\n }\n return result;\n } finally {\n seen.delete(obj);\n }\n }\n}\n","/**\n * Internal diagnostic channel for the logging library's own errors.\n *\n * Sinks use this when they fail, instead of throwing and crashing\n * the application. By default writes to `process.stderr`.\n *\n * @example\n * ```ts\n * SelfLog.setOutput(fs.createWriteStream('./logs/selflog.txt'));\n * SelfLog.disable();\n * ```\n */\nexport const SelfLog = {\n _enabled: true,\n _output: null as null | { write(s: string): void },\n\n /**\n * Writes an internal diagnostic message. Includes optional error details.\n *\n * @param message - diagnostic message\n * @param error - optional associated error\n */\n write(message: string, error?: unknown): void {\n if (!this._enabled) return;\n const timestamp = new Date().toISOString();\n const errorStr =\n error instanceof Error\n ? ` ${error.stack ?? error.message}`\n : error !== undefined\n ? ` ${String(error)}`\n : '';\n const line = `[${timestamp} SelfLog] ${message}${errorStr}\\n`;\n if (this._output) {\n this._output.write(line);\n } else if (typeof process !== 'undefined' && process.stderr) {\n process.stderr.write(line);\n }\n },\n\n /**\n * Sets a custom output stream for self-diagnostics.\n *\n * @param output - writable stream with a `write` method\n */\n setOutput(output: { write(s: string): void }): void {\n this._output = output;\n },\n\n /** Suppresses all internal diagnostic output. */\n disable(): void {\n this._enabled = false;\n },\n\n /** Re-enables internal diagnostic output. */\n enable(): void {\n this._enabled = true;\n }\n};\n","import type { LogEvent } from '../LogEvent.js';\nimport { SelfLog } from '../SelfLog.js';\nimport type { LogSink } from '../Sink.js';\n\n/**\n * Configuration for the batching sink wrapper.\n */\nexport interface BatchingSinkOptions {\n /** Flush after this many events. @default 100 */\n batchSize?: number;\n /** Flush after this many milliseconds of inactivity. @default 2000 */\n flushInterval?: number;\n /** Maximum buffered events before dropping. @default 50000 */\n maxQueueSize?: number;\n /** Maximum retry attempts for failed flushes. @default 5 */\n maxRetries?: number;\n /** Initial retry delay in ms (exponential backoff). @default 1000 */\n retryDelay?: number;\n /** Consecutive failures before circuit breaker opens. @default 3 */\n circuitBreakerThreshold?: number;\n /** The emit function that writes a batch to the target. */\n emit: (batch: LogEvent[]) => Promise<void>;\n}\n\n/**\n * Production-grade batching wrapper for log sinks.\n *\n * Provides buffering, retry with exponential backoff, and circuit\n * breaking. All network/file sinks use this internally.\n *\n * @example\n * ```ts\n * const sink = new BatchingSink({\n * batchSize: 100,\n * flushInterval: 2000,\n * emit: async (batch) => {\n * await fetch('/logs', { method: 'POST', body: JSON.stringify(batch) });\n * },\n * });\n * ```\n */\nexport class BatchingSink implements LogSink {\n readonly #emitFn: (batch: LogEvent[]) => Promise<void>;\n readonly #batchSize: number;\n readonly #flushInterval: number;\n readonly #maxQueueSize: number;\n readonly #maxRetries: number;\n readonly #retryDelay: number;\n readonly #circuitBreakerThreshold: number;\n readonly #buffer: LogEvent[] = [];\n #timer: ReturnType<typeof setTimeout> | undefined;\n #consecutiveFailures = 0;\n #circuitOpen = false;\n #circuitResetTimer: ReturnType<typeof setTimeout> | undefined;\n #disposed = false;\n\n constructor(options: BatchingSinkOptions) {\n this.#emitFn = options.emit;\n this.#batchSize = options.batchSize ?? 100;\n this.#flushInterval = options.flushInterval ?? 2_000;\n this.#maxQueueSize = options.maxQueueSize ?? 50_000;\n this.#maxRetries = options.maxRetries ?? 5;\n this.#retryDelay = options.retryDelay ?? 1_000;\n this.#circuitBreakerThreshold = options.circuitBreakerThreshold ?? 3;\n }\n\n async emit(events: LogEvent[]): Promise<void> {\n if (this.#disposed) return;\n\n for (const event of events) {\n if (this.#buffer.length >= this.#maxQueueSize) {\n this.#buffer.shift(); // drop oldest\n }\n this.#buffer.push(event);\n }\n\n if (this.#buffer.length >= this.#batchSize) {\n await this.flush();\n } else {\n this.#resetTimer();\n }\n }\n\n async flush(): Promise<void> {\n this.#clearTimer();\n\n while (this.#buffer.length > 0) {\n if (this.#circuitOpen) {\n SelfLog.write(\n 'BatchingSink circuit breaker open — deferring flush'\n );\n return;\n }\n\n const batch = this.#buffer.splice(0, this.#batchSize);\n\n let lastError: unknown;\n let emitted = false;\n for (let attempt = 0; attempt <= this.#maxRetries; attempt++) {\n try {\n await this.#emitFn(batch);\n this.#consecutiveFailures = 0;\n emitted = true;\n break;\n } catch (err) {\n lastError = err;\n if (attempt < this.#maxRetries) {\n const delay = this.#retryDelay * 2 ** attempt;\n await new Promise(r => setTimeout(r, delay));\n }\n }\n }\n\n if (!emitted) {\n this.#consecutiveFailures++;\n SelfLog.write(\n `BatchingSink flush failed after ${this.#maxRetries + 1} attempts`,\n lastError\n );\n\n if (\n this.#consecutiveFailures >= this.#circuitBreakerThreshold\n ) {\n this.#circuitOpen = true;\n SelfLog.write(\n `BatchingSink circuit breaker opened — backing off 30s`\n );\n this.#circuitResetTimer = setTimeout(() => {\n this.#circuitOpen = false;\n this.#consecutiveFailures = 0;\n SelfLog.write('BatchingSink circuit breaker closed');\n if (this.#buffer.length > 0) {\n this.flush().catch(err => {\n SelfLog.write(\n 'BatchingSink post-circuit flush failed',\n err\n );\n });\n }\n }, 30_000);\n if (this.#circuitResetTimer.unref) {\n this.#circuitResetTimer.unref();\n }\n }\n return;\n }\n }\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n this.#disposed = true;\n this.#clearTimer();\n if (this.#circuitResetTimer) {\n clearTimeout(this.#circuitResetTimer);\n }\n // Flush remaining events (best-effort)\n if (this.#buffer.length > 0 && !this.#circuitOpen) {\n try {\n await this.#emitFn(this.#buffer.splice(0, this.#buffer.length));\n } catch (err) {\n SelfLog.write('BatchingSink dispose flush failed', err);\n }\n }\n }\n\n #resetTimer(): void {\n this.#clearTimer();\n this.#timer = setTimeout(() => {\n this.flush().catch(err => {\n SelfLog.write('BatchingSink timer flush failed', err);\n });\n }, this.#flushInterval);\n if (this.#timer.unref) {\n this.#timer.unref();\n }\n }\n\n #clearTimer(): void {\n if (this.#timer) {\n clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n }\n}\n"],"mappings":"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","names":["LogLevel","levelNameMap","levelStringMap","parseLogLevel","name","normalized","level","levelToString","levelToShortString","safeSerialize","value","options","maxDepth","maxStringLength","seen","serialize","val","depth","serializeObject","obj","result","key","item","SelfLog","message","error","timestamp","errorStr","line","output","BatchingSink","#emitFn","#batchSize","#flushInterval","#maxQueueSize","#maxRetries","#retryDelay","#circuitBreakerThreshold","#buffer","#timer","#consecutiveFailures","#circuitOpen","#circuitResetTimer","#disposed","options","events","event","#resetTimer","#clearTimer","SelfLog","batch","lastError","emitted","attempt","err","delay","r"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type ClickHouseColumnMapping, type ClickHouseSinkOptions, type CreateLogsTableOptions, clickHouseSink, createLogsTable } from './sinks/ClickHouseSink.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import{c as l,e as p,g as c}from"./chunk-EU6TDBKQ.js";var g={timestamp:"timestamp",level:"level",messageTemplate:"message_template",renderedMessage:"rendered_message",properties:"properties",exception:"exception",correlationId:"correlation_id",traceId:"trace_id",spanId:"span_id",sourceContext:"source_context"};async function m(i,t,r){let a=r?.engine??"MergeTree",n=r?.partitionBy??"toYYYYMM(timestamp)",s=r?.orderBy??["timestamp","level"],o=r?.ttl,e=`CREATE TABLE IF NOT EXISTS ${t} (
|
|
2
|
+
timestamp DateTime64(3, 'UTC'),
|
|
3
|
+
level LowCardinality(String),
|
|
4
|
+
message_template String,
|
|
5
|
+
rendered_message String,
|
|
6
|
+
source_context LowCardinality(String) DEFAULT '',
|
|
7
|
+
properties String DEFAULT '{}',
|
|
8
|
+
exception Nullable(String),
|
|
9
|
+
correlation_id Nullable(String),
|
|
10
|
+
trace_id Nullable(String),
|
|
11
|
+
span_id Nullable(String)
|
|
12
|
+
) ENGINE = ${a}
|
|
13
|
+
PARTITION BY ${n}
|
|
14
|
+
ORDER BY (${s.join(", ")})`;o&&(e+=`
|
|
15
|
+
TTL ${o}`),await i.raw(e)}function d(i){let t={...g,...i.columns},r=i.table,a=i.connection,n=new c({batchSize:i.batchSize??1e3,flushInterval:i.flushInterval??5e3,emit:async s=>{let o=s.map(e=>({[t.timestamp]:e.timestamp,[t.level]:l(e.level),[t.messageTemplate]:e.messageTemplate,[t.renderedMessage]:e.renderedMessage,[t.sourceContext]:e.properties.SourceContext??"",[t.properties]:JSON.stringify(p(e.properties)),[t.exception]:e.exception?.stack??e.exception?.message??null,[t.correlationId]:e.properties.CorrelationId??null,[t.traceId]:e.properties.TraceId??null,[t.spanId]:e.properties.SpanId??null}));await a(r).insert(o)}});return{async emit(s){await n.emit(s)},async flush(){await n.flush()},async[Symbol.asyncDispose](){await n[Symbol.asyncDispose]()}}}export{d as clickHouseSink,m as createLogsTable};
|
|
16
|
+
//# sourceMappingURL=clickhouse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sinks/ClickHouseSink.ts"],"sourcesContent":["import type { LogEvent } from '../LogEvent.js';\nimport { levelToString } from '../LogLevel.js';\nimport type { LogSink } from '../Sink.js';\nimport { safeSerialize } from '../serialization.js';\nimport { BatchingSink } from './BatchingSink.js';\n\n/**\n * Column mapping for the ClickHouse logs table.\n */\nexport interface ClickHouseColumnMapping {\n timestamp?: string;\n level?: string;\n messageTemplate?: string;\n renderedMessage?: string;\n properties?: string;\n exception?: string;\n correlationId?: string;\n traceId?: string;\n spanId?: string;\n sourceContext?: string;\n}\n\n/**\n * ClickHouse sink configuration.\n */\nexport interface ClickHouseSinkOptions {\n /** Knex-ClickHouse connection instance. */\n connection: any;\n /** Table name to insert into. */\n table: string;\n /** Events per batch. @default 1000 */\n batchSize?: number;\n /** Max milliseconds between flushes. @default 5000 */\n flushInterval?: number;\n /** Custom column mapping. */\n columns?: ClickHouseColumnMapping;\n}\n\n/**\n * Options for creating the ClickHouse logs table.\n */\nexport interface CreateLogsTableOptions {\n /** Table engine. @default 'MergeTree' */\n engine?: string;\n /** Partition expression. @default \"toYYYYMM(timestamp)\" */\n partitionBy?: string;\n /** ORDER BY columns. @default ['timestamp', 'level'] */\n orderBy?: string[];\n /** TTL expression for automatic data expiration. */\n ttl?: string;\n}\n\nconst DEFAULT_COLUMNS: Required<ClickHouseColumnMapping> = {\n timestamp: 'timestamp',\n level: 'level',\n messageTemplate: 'message_template',\n renderedMessage: 'rendered_message',\n properties: 'properties',\n exception: 'exception',\n correlationId: 'correlation_id',\n traceId: 'trace_id',\n spanId: 'span_id',\n sourceContext: 'source_context'\n};\n\n/**\n * Creates the ClickHouse logs table with a recommended schema.\n *\n * @param connection - knex-clickhouse connection\n * @param tableName - name for the logs table\n * @param options - DDL options (engine, partitioning, TTL)\n *\n * @example\n * ```ts\n * await createLogsTable(ch, 'application_logs', {\n * ttl: 'timestamp + INTERVAL 90 DAY',\n * });\n * ```\n */\nexport async function createLogsTable(\n connection: any,\n tableName: string,\n options?: CreateLogsTableOptions\n): Promise<void> {\n const engine = options?.engine ?? 'MergeTree';\n const partitionBy = options?.partitionBy ?? 'toYYYYMM(timestamp)';\n const orderBy = options?.orderBy ?? ['timestamp', 'level'];\n const ttl = options?.ttl;\n\n let ddl = `CREATE TABLE IF NOT EXISTS ${tableName} (\n timestamp DateTime64(3, 'UTC'),\n level LowCardinality(String),\n message_template String,\n rendered_message String,\n source_context LowCardinality(String) DEFAULT '',\n properties String DEFAULT '{}',\n exception Nullable(String),\n correlation_id Nullable(String),\n trace_id Nullable(String),\n span_id Nullable(String)\n) ENGINE = ${engine}\nPARTITION BY ${partitionBy}\nORDER BY (${orderBy.join(', ')})`;\n\n if (ttl) {\n ddl += `\\nTTL ${ttl}`;\n }\n\n await connection.raw(ddl);\n}\n\n/**\n * Creates a ClickHouse sink that batch inserts log events.\n *\n * Ships as a separate entrypoint (`@cleverbrush/log/clickhouse`) to\n * avoid forcing `@cleverbrush/knex-clickhouse` as a dependency.\n *\n * @param options - ClickHouse connection and batching configuration\n * @returns a `LogSink` that batch inserts into ClickHouse\n *\n * @example\n * ```ts\n * const sink = clickHouseSink({\n * connection: ch,\n * table: 'application_logs',\n * batchSize: 1000,\n * });\n * ```\n */\nexport function clickHouseSink(options: ClickHouseSinkOptions): LogSink {\n const cols = { ...DEFAULT_COLUMNS, ...options.columns };\n const table = options.table;\n const conn = options.connection;\n\n const batcher = new BatchingSink({\n batchSize: options.batchSize ?? 1_000,\n flushInterval: options.flushInterval ?? 5_000,\n emit: async (batch: LogEvent[]) => {\n const rows = batch.map(event => ({\n [cols.timestamp]: event.timestamp,\n [cols.level]: levelToString(event.level),\n [cols.messageTemplate]: event.messageTemplate,\n [cols.renderedMessage]: event.renderedMessage,\n [cols.sourceContext]:\n (event.properties.SourceContext as string) ?? '',\n [cols.properties]: JSON.stringify(\n safeSerialize(event.properties)\n ),\n [cols.exception]:\n event.exception?.stack ?? event.exception?.message ?? null,\n [cols.correlationId]:\n (event.properties.CorrelationId as string) ?? null,\n [cols.traceId]: (event.properties.TraceId as string) ?? null,\n [cols.spanId]: (event.properties.SpanId as string) ?? null\n }));\n\n await conn(table).insert(rows);\n }\n });\n\n return {\n async emit(events: LogEvent[]): Promise<void> {\n await batcher.emit(events);\n },\n\n async flush(): Promise<void> {\n await batcher.flush();\n },\n\n async [Symbol.asyncDispose](): Promise<void> {\n await batcher[Symbol.asyncDispose]();\n }\n };\n}\n"],"mappings":"sDAoDA,IAAMA,EAAqD,CACvD,UAAW,YACX,MAAO,QACP,gBAAiB,mBACjB,gBAAiB,mBACjB,WAAY,aACZ,UAAW,YACX,cAAe,iBACf,QAAS,WACT,OAAQ,UACR,cAAe,gBACnB,EAgBA,eAAsBC,EAClBC,EACAC,EACAC,EACa,CACb,IAAMC,EAASD,GAAS,QAAU,YAC5BE,EAAcF,GAAS,aAAe,sBACtCG,EAAUH,GAAS,SAAW,CAAC,YAAa,OAAO,EACnDI,EAAMJ,GAAS,IAEjBK,EAAM,8BAA8BN,CAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAWxCE,CAAM;AAAA,eACJC,CAAW;AAAA,YACdC,EAAQ,KAAK,IAAI,CAAC,IAEtBC,IACAC,GAAO;AAAA,MAASD,CAAG,IAGvB,MAAMN,EAAW,IAAIO,CAAG,CAC5B,CAoBO,SAASC,EAAeN,EAAyC,CACpE,IAAMO,EAAO,CAAE,GAAGX,EAAiB,GAAGI,EAAQ,OAAQ,EAChDQ,EAAQR,EAAQ,MAChBS,EAAOT,EAAQ,WAEfU,EAAU,IAAIC,EAAa,CAC7B,UAAWX,EAAQ,WAAa,IAChC,cAAeA,EAAQ,eAAiB,IACxC,KAAM,MAAOY,GAAsB,CAC/B,IAAMC,EAAOD,EAAM,IAAIE,IAAU,CAC7B,CAACP,EAAK,SAAS,EAAGO,EAAM,UACxB,CAACP,EAAK,KAAK,EAAGQ,EAAcD,EAAM,KAAK,EACvC,CAACP,EAAK,eAAe,EAAGO,EAAM,gBAC9B,CAACP,EAAK,eAAe,EAAGO,EAAM,gBAC9B,CAACP,EAAK,aAAa,EACdO,EAAM,WAAW,eAA4B,GAClD,CAACP,EAAK,UAAU,EAAG,KAAK,UACpBS,EAAcF,EAAM,UAAU,CAClC,EACA,CAACP,EAAK,SAAS,EACXO,EAAM,WAAW,OAASA,EAAM,WAAW,SAAW,KAC1D,CAACP,EAAK,aAAa,EACdO,EAAM,WAAW,eAA4B,KAClD,CAACP,EAAK,OAAO,EAAIO,EAAM,WAAW,SAAsB,KACxD,CAACP,EAAK,MAAM,EAAIO,EAAM,WAAW,QAAqB,IAC1D,EAAE,EAEF,MAAML,EAAKD,CAAK,EAAE,OAAOK,CAAI,CACjC,CACJ,CAAC,EAED,MAAO,CACH,MAAM,KAAKI,EAAmC,CAC1C,MAAMP,EAAQ,KAAKO,CAAM,CAC7B,EAEA,MAAM,OAAuB,CACzB,MAAMP,EAAQ,MAAM,CACxB,EAEA,MAAO,OAAO,YAAY,GAAmB,CACzC,MAAMA,EAAQ,OAAO,YAAY,EAAE,CACvC,CACJ,CACJ","names":["DEFAULT_COLUMNS","createLogsTable","connection","tableName","options","engine","partitionBy","orderBy","ttl","ddl","clickHouseSink","cols","table","conn","batcher","BatchingSink","batch","rows","event","levelToString","safeSerialize","events"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a new correlation ID using UUID v7 (time-sortable).
|
|
3
|
+
*
|
|
4
|
+
* Falls back to UUID v4 if the runtime doesn't support v7.
|
|
5
|
+
*
|
|
6
|
+
* @returns a new UUID string suitable for correlation
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateCorrelationId(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Extracts a correlation ID from incoming HTTP request headers.
|
|
11
|
+
*
|
|
12
|
+
* Checks headers in priority order:
|
|
13
|
+
* 1. `X-Correlation-Id`
|
|
14
|
+
* 2. `X-Request-Id`
|
|
15
|
+
* 3. `traceparent` (W3C Trace Context — extracts the trace-id segment)
|
|
16
|
+
*
|
|
17
|
+
* If no header is found, generates a new correlation ID.
|
|
18
|
+
*
|
|
19
|
+
* @param headers - request headers object
|
|
20
|
+
* @returns a correlation ID string
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractCorrelationId(headers: Record<string, string | string[] | undefined>): string;
|