first commit
This commit is contained in:
80
docs/observability/monitor/logger/src/sink/json-sink.md
Normal file
80
docs/observability/monitor/logger/src/sink/json-sink.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# json-sink
|
||||
|
||||
## Purpose
|
||||
|
||||
`JsonSink` is the machine-readable structured output sink for `@suaveplan/logger`. It serialises each `LogRecord` to a single JSON line and passes it to a configurable `writer` callback — defaulting to `process.stdout.write` — producing newline-delimited JSON (NDJSON) that is directly ingestible by log aggregators such as Fluentd, Logstash, Loki, or Datadog.
|
||||
|
||||
The design is deliberately thin: one serialisation step, one write call, no buffering, no formatting decisions. Buffering is the responsibility of `BufferedSink`; formatting decisions belong to the caller. `JsonSink` is the composable primitive that sits at the bottom of a sink stack.
|
||||
|
||||
## Features
|
||||
|
||||
- Serialises `LogRecord` to a compact JSON line via `JSON.stringify`
|
||||
- Pluggable `writer` callback — defaults to `process.stdout.write` with a trailing newline
|
||||
- Throws a structured `LoggerError(LOGGER.SERIALIZE)` if serialisation fails (e.g., circular references in `ctx`)
|
||||
- Synchronous `write` — no buffering, no internal state; each call is independent
|
||||
- Trivially composable with `BufferedSink` for async, bounded queuing
|
||||
- No external runtime dependencies beyond the `LogRecord` type and `LoggerError`
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { JsonSink } from "@suaveplan/logger/sink";
|
||||
|
||||
const sink = new JsonSink();
|
||||
// Writes: {"ts":"2024-01-01T00:00:00.000Z","level":"info","msg":"hello","ctx":{}}\n
|
||||
|
||||
sink.write({
|
||||
ts: new Date().toISOString(),
|
||||
level: "info",
|
||||
msg: "hello",
|
||||
ctx: {},
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { JsonSink } from "@suaveplan/logger/sink";
|
||||
import { BufferedSink } from "@suaveplan/logger/sink";
|
||||
import { createWriteStream } from "node:fs";
|
||||
|
||||
// Write NDJSON to a rotating log file
|
||||
const stream = createWriteStream("/var/log/app/app.ndjson", { flags: "a" });
|
||||
const json = new JsonSink({
|
||||
writer: (line) => stream.write(line + "\n"),
|
||||
});
|
||||
|
||||
// Wrap in a buffer to absorb bursts
|
||||
const buffered = new BufferedSink({ sink: json, maxQueueSize: 2000 });
|
||||
|
||||
// Forward records from the logger to the buffered sink
|
||||
logger.addSink(buffered);
|
||||
|
||||
// Drain and close on process exit
|
||||
process.on("SIGTERM", async () => {
|
||||
await buffered.close();
|
||||
stream.end();
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `class JsonSink implements Sink`
|
||||
|
||||
**Constructor**: `new JsonSink(options?: JsonSinkOptions)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `writer` | `(line: string) => void` | `process.stdout.write(line + "\n")` | Receives each serialised JSON line |
|
||||
|
||||
**Methods**
|
||||
|
||||
- `write(record: LogRecord): void` — Serialise the record and invoke the writer callback. Throws `LoggerError` with code `LOGGER.SERIALIZE` if `JSON.stringify` throws (e.g., due to a circular reference inside `ctx`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The default `writer` uses `process.stdout.write` rather than `console.log` to avoid the extra newline that `console.log` appends to its own output and to prevent interference with any `console` mocking installed by test frameworks.
|
||||
|
||||
`JsonSink` does not implement `flush` or `close` — it has no internal buffer to drain. When wrapped in `BufferedSink`, those lifecycle methods are handled by the buffer layer. This clean separation of concerns keeps `JsonSink` trivially testable with a synchronous `writer` stub.
|
||||
|
||||
Because `JSON.stringify` can throw on circular structures, `JsonSink` wraps the call in a try/catch and re-throws as a typed `LoggerError` so callers can distinguish serialisation failures from other sink errors without inspecting error message strings.
|
||||
Reference in New Issue
Block a user