Initial Commit
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# `server` (main-side)
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The main-side server. Consumes inbound `log-batch` frames from a
|
||||
worker-isolated plugin, applies redaction at the boundary, narrows the
|
||||
wire-format levels (`trace` / `fatal`) down to the `LoggerLike` levels
|
||||
(`debug` / `error`), and forwards each record to the host's main-side
|
||||
`@suaveplan/logger` instance. Plugin pre-tagging is honoured by routing
|
||||
through `logger.child(plugin)` when available, so every record reaches
|
||||
the sink with structured `plugin` / `plugin_version` fields without
|
||||
the worker having to know about the host logger's child-binding
|
||||
semantics.
|
||||
|
||||
The server is best-effort, just like the worker-side client. Malformed
|
||||
frames increment a counter, fire an `onInvalidFrame` hook, and surface
|
||||
via `onError` — but they NEVER throw out of the listener. A single bad
|
||||
record cannot bring down the port.
|
||||
|
||||
## Features
|
||||
|
||||
- Wire-frame validation via Zod (`parseLoggerProxyFrame`).
|
||||
- Level narrowing (`trace → debug`, `fatal → error`) so downstream
|
||||
sinks need not understand the broader level set.
|
||||
- Optional redaction passthrough at the boundary.
|
||||
- Child-logger caching per `PluginTag` (Map keyed by
|
||||
`name@version` so caching survives structured-clone of the tag
|
||||
object across the port boundary).
|
||||
- Hard `maxBatchSize` cap (default 10_000 records/batch) that protects
|
||||
the main logger from a hostile or buggy worker dumping unbounded
|
||||
inbound traffic.
|
||||
- Observation hooks: `onRecord` (post-redaction), `onError`, and
|
||||
`onInvalidFrame`.
|
||||
- `forwardedCount`, `invalidFrameCount`, `overflowDropCount` counters.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createLoggerProxyServer } from "@suaveplan/api-logger-worker-proxy/main-side";
|
||||
import { createLogger } from "@suaveplan/logger";
|
||||
|
||||
const mainLogger = createLogger({ level: "info" });
|
||||
const server = createLoggerProxyServer(mainPort, mainLogger);
|
||||
// ... worker emits log records over workerPort ...
|
||||
server.close();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Pipe through host redaction and observe every record:
|
||||
|
||||
```ts
|
||||
import { createLoggerProxyServer } from "@suaveplan/api-logger-worker-proxy/main-side";
|
||||
import { maskKeys } from "@suaveplan/api-logger-worker-proxy/redaction-passthrough";
|
||||
|
||||
const server = createLoggerProxyServer(mainPort, mainLogger, {
|
||||
redaction: {
|
||||
ctxRedactor: maskKeys(["password", "token", "secret"]),
|
||||
},
|
||||
onRecord: (rec, plug) => audit.write(rec, plug),
|
||||
onError: (err) => mainLogger.warn("logger-proxy error", { code: err.code }),
|
||||
onInvalidFrame: (raw, cause) =>
|
||||
mainLogger.warn("logger-proxy invalid frame", { raw, cause }),
|
||||
maxBatchSize: 5_000,
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createLoggerProxyServer(port, logger, options?): LoggerProxyServer`
|
||||
— primary entry.
|
||||
- `LoggerProxyServer.close()` — detach. Idempotent.
|
||||
- `OnRecord(record, plugin)` — post-redaction, pre-dispatch hook.
|
||||
- `OnInvalidFrame(input, error)` — fires on Zod parse failure.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`logger.child` is optional on `LoggerLike`; when absent the server
|
||||
merges `plugin` / `plugin_version` into the call-site `ctx` instead.
|
||||
The child-logger cache is a `Map` keyed by `${name}@${version}` so
|
||||
that workers reusing the same identity (the common case) hit the
|
||||
cache even after structured-clone has produced a fresh object on the
|
||||
receive side of the port. Sink
|
||||
failures (the host logger throws while writing) are caught per-record
|
||||
so a single bad record cannot stall the listener; the error surfaces
|
||||
via `onError`.
|
||||
Reference in New Issue
Block a user