Structured Logs
Use createStructuredLog() when multiple log lines belong to one logical unit of work and you want one final structured payload.
Structured logs emit nothing until
.emit()is called. Everything you.set()is held silently and flushed as a single rich event.
Root import
createStructuredLog is a root export:
import { createStructuredLog } from "@blyp/core";Standalone usage
import { createStructuredLog } from "@blyp/core";
const structuredLog = createStructuredLog("checkout", {
service: "web-api",
level: "info",
timestamp: new Date().toISOString(),
});
structuredLog.set({
user: { id: 1, plan: "pro" },
cart: { items: 3, total: 9999 },
payment: { method: "card", status: "success" },
});
structuredLog.info("payment authorized");
structuredLog.emit({
status: 200,
message: "checkout",
});Nothing is written until you call .emit(). The intermediate info(), warn(), error(), and table() calls accumulate events on the in-memory batch.
Without structured logs - scattered output, hard to correlate:
[INFO] user authenticated
[INFO] item added to cart
[INFO] payment processing
[INFO] order createdWith structured logs - one rich event on .emit():
[INFO] checkout
service=web-api
user.id=1 user.plan=pro
cart.items=3 cart.total=9999
payment.method=card payment.status=success
status=200Terminal output on .emit():
[INFO] checkout
service=web-api
user.id=1 user.plan=pro
cart.items=3 cart.total=9999
payment.method=card payment.status=success
status=200Typed fields
If you want the final payload to be strongly typed, pass a generic:
import { createStructuredLog } from "@blyp/core";
const structuredLog = createStructuredLog<{
message: string;
level: string;
timestamp: string;
hostname?: string;
port?: number;
}>("test", {
message: "Hello Elysia",
level: "info",
timestamp: new Date().toISOString(),
hostname: "127.0.0.1",
port: 3000,
});set() extends the field type, so later additions are reflected in the returned StructuredLog<TFields> instance.
What happens if you forget .emit()
If a request or workflow completes without calling .emit(), Blyp does not flush the structured payload.
(no structured log output - .emit() was never called)This is the main failure mode to watch for during development: every set(), info(), warn(), error(), or table() call stays in memory until the final emit happens.
Framework handler behavior
Inside framework handlers, the imported createStructuredLog(...) automatically binds to the active request-scoped logger.
- request metadata such as method, path, status, and duration is merged when you emit
- framework
customPropsare merged into the final payload - a structured emit suppresses the normal auto
http_requestorhttp_errorrecord for that request - if you start a request-scoped structured log and then call the root
loggerin the same request, Blyp warns once and ignores the root logger write
That means you can keep the same root import in standalone code and in framework routes without switching APIs.
Emit options
emit() accepts the fields Blyp uses to finalize the payload:
structuredLog.emit({
response,
status: 200,
level: "success",
message: "checkout completed",
error,
});Use:
responseorstatusto set the final HTTP statuserrorto attach normalized error detailslevelandmessageto override the final emitted event summary
Emitted payload shape
The returned payload contains your typed fields plus Blyp metadata:
import type {
StructuredLog,
StructuredLogEmitOptions,
StructuredLogEvent,
StructuredLogPayload,
} from "@blyp/core";StructuredLogPayload<TFields> includes:
- your initial and
set()fields groupIdtimestamplevel- optional
method,path,status, andduration - optional
eventscollected before emit - optional normalized
error
Production output (NDJSON)
In production, the same structured log emits as a single NDJSON line:
{"level":"info","time":1710000000000,"msg":"checkout","service":"web-api","user":{"id":1,"plan":"pro"},"cart":{"items":3,"total":9999},"payment":{"method":"card","status":"success"},"status":200}This is what gets written to your log file or forwarded to a connector such as PostHog, Sentry, Better Stack, or OTLP.