Enterprise structured logging for TypeScript — Serilog-style message templates, CLEF format, batching sinks with circuit breaking, ambient correlation IDs.
npm install @cleverbrush/log
import {
createLogger,
consoleSink,
hostnameEnricher,
processIdEnricher,
} from '@cleverbrush/log';
const logger = createLogger({
minimumLevel: 'information',
sinks: [consoleSink({ theme: 'dark' })],
enrichers: [hostnameEnricher(), processIdEnricher()],
});
logger.info('Server started on port {Port}', { Port: 3000 });
logger.error(new Error('oops'), 'Request failed for {UserId}', { UserId: 42 });
await logger.dispose();
{Named} properties captured as structured dataTypedTemplate<T> via @cleverbrush/schema for compile-time checked, groupable log events@cleverbrush/serverconfigureLogging() for @cleverbrush/diPass 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, SigNoz, ClickHouse, etc., while the rendered message is interpolated as usual.
import { s } from '@cleverbrush/schema';
import { createLogger, consoleSink } from '@cleverbrush/log';
// Define once — compile-time checked parameter types
const TodoCreated = s.parseString('Todo #{TodoId} "{Title}" created by {UserId}');
const logger = createLogger({ sinks: [consoleSink()] });
// TypeScript enforces { TodoId, Title, UserId }
logger.info(TodoCreated, { TodoId: 1, Title: 'Buy milk', UserId: 'u-42' });
import { useLogging } from '@cleverbrush/log';
// Returns [correlationIdMiddleware, requestLoggingMiddleware]
const [correlationId, requestLogging] = useLogging(logger, {
excludePaths: ['/health'],
// Set to false when OTel traceparent already provides traceability
correlationResponseHeader: false,
});
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.
Trace correlation, OTLP log export, and the traceEnricher (which copies the active span's traceId / spanId onto every log event) live in @cleverbrush/otel. Wire them up like this:
import { createLogger, consoleSink } from '@cleverbrush/log';
import { otelLogSink, traceEnricher } from '@cleverbrush/otel';
const logger = createLogger({
sinks: [consoleSink(), otelLogSink()],
enrichers: [traceEnricher()],
});
@cleverbrush/log itself stays free of any OpenTelemetry runtime dependency.
BSD-3-Clause