@mikkurogue/tracing-core
v0.0.2
Published
A runtime-agnostic tracing library for TypeScript/JavaScript, inspired by Rust's tracing crate
Maintainers
Readme
@tracing/core
A runtime-agnostic tracing library for TypeScript/JavaScript, inspired by Rust's tracing crate.
Features
- Runtime agnostic - Works in Node.js, Bun, Deno, browsers, and edge runtimes
- Structured logging - Attach typed fields to events, not just strings
- Spans - Track operations over time with nested context
- Pluggable subscribers - Implement your own trace processors
- Zero dependencies - No external runtime dependencies
- TypeScript first - Full type safety with comprehensive type definitions
Installation
npm install @mikkurogue/tracing-coreQuick Start
import {
setGlobalSubscriber,
ConsoleSubscriber,
Level,
info,
warn,
error,
span
} from "@mikkurogue/tracing-core";
// Initialize with a console subscriber
setGlobalSubscriber(new ConsoleSubscriber({
minLevel: Level.DEBUG,
timestamp: "time"
}));
// Log events
info("Server starting", { target: "http::server", fields: { port: 3000 } });
warn("Cache miss", { target: "cache", fields: { key: "user:123" } });
error("Connection failed", { target: "db", fields: { error: "timeout" } });
// Or use simple fields syntax
info("Request received", { method: "GET", path: "/api/users" });Output:
14:30:45.123 INFO http::server: Server starting port=3000
14:30:45.124 WARN cache: Cache miss key="user:123"
14:30:45.125 ERROR db: Connection failed error="timeout"
14:30:45.126 INFO Request received method="GET" path="/api/users"Spans
Spans represent a period of time and provide context for events:
import { span, info, debug } from "@mikkurogue/tracing-core";
// Using run() for automatic enter/exit
span("handle_request", { method: "GET" }).run(() => {
debug("Parsing request");
// Nested span
span("db_query", { table: "users" }).run(() => {
info("Executing query");
});
});
// Manual enter/exit
const s = span("long_operation");
s.enter();
// ... do work ...
s.exit();
// Async operations
await span("fetch_data").runAsync(async () => {
const data = await fetch("/api/data");
return data.json();
});Log Levels
Five levels available (from most to least verbose):
import { trace, debug, info, warn, error } from "@mikkurogue/tracing-core";
trace("Very detailed info"); // Level.TRACE
debug("Debugging info"); // Level.DEBUG
info("General info"); // Level.INFO
warn("Warning"); // Level.WARN
error("Error occurred"); // Level.ERRORConsole Subscriber Options
new ConsoleSubscriber({
// Minimum level to display (default: Level.INFO)
minLevel: Level.DEBUG,
// Enable/disable ANSI colors (default: true)
colors: true,
// Timestamp format (default: "datetime")
// Options: "iso", "datetime", "time", "time-short", "unix", "unix-secs", false
// Or provide a custom function: (ts: number) => string
timestamp: "time",
// Show target names (default: true)
targets: true,
});Timestamp Formats
| Format | Example |
|--------|---------|
| "datetime" | Feb 19 14:30:45.123 |
| "iso" | 2024-02-19T14:30:45.123Z |
| "time" | 14:30:45.123 |
| "time-short" | 14:30:45 |
| "unix" | 1708354245123 |
| "unix-secs" | 1708354245 |
| false | (no timestamp) |
| (ts) => string | Custom formatter |
Function Instrumentation
Automatically wrap functions with spans:
import { instrument, instrumentAsync } from "@mikkurogue/tracing-core";
// Sync function
const processItem = instrument("process_item", (item: Item) => {
// ... processing logic
return result;
});
// Async function
const fetchUser = instrumentAsync("fetch_user", async (id: string) => {
const response = await fetch(`/users/${id}`);
return response.json();
});Custom Subscribers
Implement the Subscriber interface to create custom trace processors:
import type { Subscriber, SpanContext, Event, Metadata } from "@tracing/core";
class MySubscriber implements Subscriber {
enabled(metadata: Metadata): boolean {
return metadata.level >= Level.INFO;
}
onSpanEnter(span: SpanContext): void {
// Called when a span is entered
}
onSpanExit(span: SpanContext): void {
// Called when a span is exited
}
onEvent(event: Event): void {
// Called for each log event
// Send to external service, write to file, etc.
}
}
setGlobalSubscriber(new MySubscriber());Testing
Use CollectorSubscriber to capture events in tests:
import { CollectorSubscriber, setGlobalSubscriber, info } from "@mikkurogue/tracing-core";
const collector = new CollectorSubscriber();
setGlobalSubscriber(collector);
// Run code that logs
info("Test event", { key: "value" });
// Assert on collected events
expect(collector.events).toHaveLength(1);
expect(collector.events[0].message).toBe("Test event");
// Clear between tests
collector.clear();License
MIT
