Runtime & Infrastructure
Logging
Logging — Runtime & Infrastructure documentation for BCP Framework.
Last updated: 2026-08-29T17:13:55
Logging and observability
BCP Framework 0.1.23 added the structured server logger and development request timing output. BCP Framework 0.2.7 complements that logging layer with process-local metrics and health/readiness primitives through bcp/observability.
Use these layers together:
bcp/server
-> structured event/request logs
bcp/observability
-> counters, gauges, histograms
-> Prometheus exposition
-> request metrics middleware
-> health/readiness checksSee Observability Platform v2 for the 0.2.7 metrics and health APIs.
Server logger
Import logging APIs from the server-only bcp/server entry:
import {
logger,
} from "bcp/server";
logger.info(
"Application started",
{
service: "catalog",
}
);Available levels are:
debug
info
warn
error
silentThe default level is info.
Structured fields can contain strings, numbers, booleans, objects, arrays, Error values and bigint values. Circular references are replaced with [Circular] rather than crashing the logger.
Request-scoped logging
Use requestLogger() while handling an API route, loader, guard or form action:
import {
requestLogger,
} from "bcp/server";
export async function loader() {
const log =
await requestLogger({
feature: "categories",
});
log.info(
"Loading categories"
);
return {
items: [],
};
}The returned child logger automatically binds the current request identity:
requestId
method
pathrequestId uses the same request-scoped value exposed by requestId(): a valid incoming X-Request-Id is reused, otherwise BCP creates a UUID for the request.
Response request ID
When an application wants to return the request identity to a caller, wrap a response with attachRequestId():
import {
attachRequestId,
json,
} from "bcp/server";
export async function GET() {
return attachRequestId(
json({
ok: true,
})
);
}The returned response includes:
X-Request-Id: <current request id>This helper preserves the original response status, status text, body and headers.
Child loggers
Bind shared fields once with child():
import {
logger,
} from "bcp/server";
const dbLog =
logger.child({
component: "database",
});
dbLog.debug(
"Executing query",
{
operation: "users.list",
}
);Child bindings are inherited by further child loggers.
Log level
Set the minimum log level with:
BCP_LOG_LEVEL=debugSupported values:
debug
info
warn
error
silentInvalid or missing values fall back to info.
Use debug during framework/application diagnostics to see development SSR and module-import timing events.
Log format
Human-readable output is the default:
BCP_LOG_FORMAT=prettyExample:
[2026-08-27T10:00:00.000Z] [INFO] [bcp] User loaded userId=42For containers, log collectors and CI systems use JSON lines:
BCP_LOG_FORMAT=jsonExample:
{"timestamp":"2026-08-27T10:00:00.000Z","level":"info","logger":"bcp","message":"User loaded","userId":42}Each log call emits exactly one line.
Development HTTP logging
Development request output remains focused on application API traffic. BCP suppresses request-style logs for framework and browser bootstrap traffic such as:
/_bcp/*
/api/bcp-styles
page/document requests
public static assetsAn application API request is converted into the structured logger format. With pretty output it resembles:
[2026-08-27T10:00:00.000Z] [INFO] [bcp-dev] HTTP request event=http.request method=GET path=/api/users status=200 durationMs=1.24With BCP_LOG_FORMAT=json the same event is emitted as one JSON object containing:
event=http.request
method
path
status
durationMsExisting development Import: ...ms and SSR: ...ms measurements are converted into debug events:
module.import
ssr.renderThey are therefore visible when BCP_LOG_LEVEL=debug and stay out of normal info output.
Metrics vs logs
Logs and metrics serve different purposes.
Use structured logs when you need per-event context:
request ID
operation
error stack
resource identity
diagnostic fieldsUse metrics when you need aggregate trends:
request count
status distribution
latency buckets
queue depth
worker countAvoid copying high-cardinality log fields such as request IDs, user IDs or raw paths into metric labels.
The 0.2.7 request metrics middleware follows this rule by using method and status labels by default.
Errors
Pass an Error as a structured field:
try {
// ...
} catch (error) {
logger.error(
"Operation failed",
{
error,
}
);
}Error values are serialized with their name, message, stack and cause when available.
Environment example
A development .env.local can use:
BCP_LOG_LEVEL=debug
BCP_LOG_FORMAT=prettyA production/container environment can use:
BCP_LOG_LEVEL=info
BCP_LOG_FORMAT=json