PAS7 Studio

Observability middleware in Bun.js: logs, request id, tracing, and latency budgets

A practical deep dive into observability middleware in Bun.js: request id, structured logs, traceparent, OpenTelemetry, latency budgets, timing headers, error correlation, redaction, sampling, and bad logging practices.

14 May 2026· 12 min read· Technology
Best forBackend engineersFull-stack developersTech leadsSRE/DevOps engineersTeams launching Bun APIs in production
Technical illustration of an observability pipeline in Bun.js with request id, logs, traces, and latency budget

Logs without request id, traces without a sampling policy, errors without tenant/user context, metrics without route groups, and debug bodies in production are not observability. They are noise that sometimes helps by accident.

Observability middleware should answer practical questions: which request, which route, which tenant, which user/API key, how long auth, rate limit, handler, database, and downstream took, which error class appeared, and where to find it in logs.

In Bun, this is better designed as a thin request-scoped layer, not as random console.log calls inside handlers.

Every response should have a correlation id.
Every error should be linked to a request id and trace id.
Every expensive middleware should have a latency budget.
No token, cookie, or API key should end up in logs.

This is the fourth chapter in the Bun middleware series. After auth and rate limiting, production behavior needs to become visible, not just “fast locally”.

Request id and correlation headers: what to generate, what to accept from edge/proxy, and what to return to the client.
Structured logs: which fields to log, how to avoid PII/secrets, and how to implement redaction. [4]
Trace context: traceparent, tracestate, W3C Trace Context, and OpenTelemetry propagation. [2][3]
Latency budgets: how to measure auth, rate limit, handler, DB, and downstream calls.
Sampling: why logging everything in a high-traffic API is dangerous and expensive.
Bad practices: console.log(req), body logs, different request ids in different layers, tracing without context propagation.

Observability should not be reduced to “we added a logger”. Logs, metrics, and traces have different roles, and middleware should collect the minimal context needed for all three.

SignalWhat it answersWhat to writeRisk
LogsWhat happened to a specific request or errorrequestId, traceId, route, status, duration, error class, tenant/user idsNoise, PII, secrets, body dumps, expensive serialization
MetricsWhat is happening in the system in aggregatelatency percentiles, error rate, request count, limiter decisionsToo much cardinality from userId/path params
TracesWhere the request spent time in a distributed flowspans, parent/child relationship, downstream calls, timingsNo value without propagation and a sampling policy

Logs, metrics, and traces do not duplicate one another. They provide different viewing angles on the same request flow.

Section observability-model screenshot

Summary

For Bun middleware, the best baseline is request id in logs, latency in metrics, and trace context in downstream calls.

A request id should be created at the entry point or accepted from a trusted edge/proxy. Then it should flow through logs, response headers, error reports, and downstream calls. If different middleware layers generate different ids, correlation is lost.

A minimal native Bun pattern:

TS
type Context = { requestId: string; startedAt: number };
type Handler = (req: Request, ctx: Context) => Promise<Response> | Response;
type Middleware = (next: Handler) => Handler;

const withRequestId: Middleware = (next) => async (req, ctx) => {
  const incoming = req.headers.get("x-request-id");
  const requestId = incoming && incoming.length <= 128 ? incoming : crypto.randomUUID();

  const res = await next(req, { ...ctx, requestId });
  res.headers.set("x-request-id", requestId);
  return res;
};

Bun.serve({
  fetch: withRequestId(async (_req, ctx) => {
    return Response.json({ ok: true, requestId: ctx.requestId });
  }),
});

In production, define the trust boundary clearly. If x-request-id comes from a public client, validate or replace it. If it comes from your gateway, you can accept it as the upstream correlation id.

Request id should be one identifier for the whole request flow: response, logs, errors, and downstream calls should reference the same id.

Section request-id screenshot

Practical rule

Generate request id once at the edge or at the Bun API entry point, then only pass it forward.

Structured logging means a log entry is a JSON-like event with fields you can filter: requestId, traceId, route, method, status, durationMs, tenantId, subjectId, errorClass. A string like user failed does not scale when an incident has 200 thousand events.

Pino is popular in the Node ecosystem specifically as a fast JSON logger and includes a redaction mechanism for hiding sensitive fields. Even if you do not use Pino directly in Bun, the principles are the same: structured fields, low overhead, redaction, sampling, and no secrets in the payload. [4]

A typical Bun API log entry shape:

TS
logger.info({
  event: "request.completed",
  requestId: ctx.requestId,
  traceId: ctx.traceId,
  method: req.method,
  route: ctx.routePattern,
  status,
  durationMs,
  tenantId: ctx.auth?.tenantId,
  subjectId: ctx.auth?.subjectId,
});

What not to log

Do not log Authorization headers, cookies, refresh tokens, full API keys, raw request bodies, passwords, payment data, or large payloads without an explicit security review.

W3C Trace Context standardizes the traceparent and tracestate headers so different systems can pass trace identity between services. OpenTelemetry uses context propagation so spans become part of one distributed trace. [2][3]

In a Bun API, this means: if a request arrives with traceparent, you should either accept it from a trusted upstream or create a new trace. If your handler calls another service, queue, worker, or AI gateway, trace context should continue downstream.

Do not confuse request id with trace id. Request id is useful for support/debugging a single response. Trace id is needed for the distributed path. They can be different, but both should appear together in logs.

Trace context matters when a request passes through multiple services. Without propagation, a trace falls apart into unrelated fragments.

Section trace-context screenshot

Summary

Request id helps find one request. Trace context shows the full path of a request through the system.

If you only see total duration, you know the API is slow, but you do not know why. Observability middleware should split a request into important stages.

01

How much credential verification costs

JWT verification, session lookup, API key lookup, tenant resolution, and permissions cache should each have their own timing. Otherwise, auth silently becomes the bottleneck.

02

How much limiter storage costs

Redis latency or local limiter overhead should be visible. If the limiter adds 15ms to every request, that is already a product decision.

03

How long business logic takes

Handler timing should be separated from middleware timing so you do not optimize the wrong layer.

04

How much time goes to DB, APIs, queues

DB and external services should have separate spans or timing events. Otherwise, a slow query looks like a slow Bun server.

Summary

Latency without breakdown turns into guesses. Breakdown without budget turns into endless noise.

In a high-traffic Bun API, full logging for every request may cost more than the handler itself. Sampling should be part of the design, not an emergency switch.

Good: full logs for errors and slow requests

Errors, 5xx, 429 spikes, auth failures, and slow requests should be logged in more detail, but still without secrets.

Careful: debug logs for all traffic

In production, this creates a storage bill, latency, noise, and security exposure. Debug sampling should be narrow and temporary.

Good: route-aware sampling

Critical flows, checkout, login, exports, and webhooks can have a different sampling policy than cheap public GET routes.

Careful: sampling without incident override

During an incident, the team should have a controlled way to temporarily increase detail for a route, tenant, or request class.

Observability can easily become the problem: slow logs, secret leaks, noise, and trace data that no one can connect.

console.log(req) or logging the raw request object.

Logging Authorization, cookies, refresh tokens, full API keys, or request body.

Generating a new request id in every middleware.

Trace id exists, but it is not written to logs.

Downstream calls do not receive trace context.

Metrics have high cardinality from raw URLs with ids or userId labels.

Error logs do not include route, status, requestId, or error class.

Debug logging is globally enabled in production.

Observability middleware runs after the handler and does not see early rejects.

No sampling policy and no storage budget.

Review rule

Observability should answer incident questions faster than it creates new performance and security risks.

Use this checklist before launching a Bun API in staging or production, especially if the API has auth, rate limiting, and downstream calls.

One request id for the whole flow

Accepted from a trusted upstream or generated at entry, added to the response and all logs.

Trace context propagates

traceparent and tracestate are accepted/created according to policy and passed into downstream calls.

Structured logs have a stable schema

event, requestId, traceId, route, method, status, durationMs, tenant/subject ids, error class.

Secrets are redacted

Authorization, cookies, API keys, tokens, passwords, PII, and raw body do not enter logs.

Latency breakdown exists

Auth, rate limit, handler, DB/downstream have separate timings or spans.

Sampling policy is defined

Errors and slow requests are logged in more detail; high-volume success logs use sampling or aggregation.

Route labels are normalized

Metrics use route patterns, for example /users/:id, not raw /users/123.

Incident override is controlled

There is a temporary way to increase detail for a route/tenant/request class without global debug chaos.

A Bun API can respond quickly, but without observability the team still loses time during incidents. Request id, structured logs, trace context, and latency breakdown make it possible to quickly understand what happened, where it happened, and who was affected.

The main trade-off: observability must not become a new bottleneck or a new data leak channel. That means middleware should be thin, redaction should be the default, sampling should be intentional, and timings should measure the layers that can actually hurt.

In production, the useful log is not the one that contains everything. It is the one that contains the right minimum and consistently connects to request, trace, route, tenant, and error.

Is `console.log` enough for a Bun API?

Not for production. `console.log` can help locally, but a production API needs structured logs with requestId, route, status, duration, error class, and redaction. Otherwise, logs are hard to search, aggregate, and store safely.

Are request id and trace id the same thing?

No. Request id is usually used for support/debug correlation of a single HTTP request. Trace id shows the distributed path across multiple services. It is best to have both in logs.

What should not be logged in observability middleware?

Authorization headers, cookies, refresh tokens, full API keys, passwords, PII, payment data, and raw request body without explicit security review. Redaction should be the default.

Do I need OpenTelemetry for every Bun API?

Not always from day one. For a small API, request id, structured logs, and latency breakdown may be enough. Once downstream services, queues, or distributed flows appear, trace context and OpenTelemetry become much more useful.

How do I avoid making observability an expensive bottleneck?

Keep middleware thin, do not serialize large objects, do not log bodies, use sampling for high-volume success traffic, measure logger overhead, and write route labels without high-cardinality values.

Where should observability middleware be placed in the pipeline?

At the very beginning, so it can see early rejects from auth/rate limit/body validation. But redaction and context enrichment must be careful not to read bodies or secrets unnecessarily.

These sources confirm trace context standards, the OpenTelemetry propagation approach, the Bun server model, and structured logging/redaction practices.

Reviewed: 14 May 2026Applies to: Bun 1.3.xApplies to: Bun.serve routesApplies to: OpenTelemetry JSApplies to: W3C Trace ContextApplies to: Pino-style structured logsTested with: Bun.serve middleware compositionTested with: Request/Response headersTested with: traceparent propagationTested with: structured JSON logsTested with: latency timing middleware

PAS7 Studio can help design observability middleware for Bun, Hono, or Elysia: request id, structured logs, trace context, latency budgets, redaction, sampling, and dashboards for production APIs.

This is especially useful for SaaS, internal platforms, integration APIs, and high-traffic products where incidents need to be explained quickly, not by reading thousands of random log lines.

You are here04/05

Observability middleware in Bun.js: logs, request id, tracing, and latency budgets

Related Articles

ai-assistants

AI Assistant Development Cost in 2026: RAG Chatbots, CRM Integrations, Guardrails, and Support

A practical buyer guide to AI assistant development cost in 2026: prototypes, RAG chatbots, knowledge-base assistants, CRM and website integrations, guardrails, evaluations, monitoring, and support.

blogs

AI for landing page development: where it speeds up launches and where it hurts conversion

A practical research piece on using AI for landing page development: v0, Webflow AI, Builder.io, Framer-like builders, UX generation, copy, SEO, personalization, A/B testing, template risk, accessibility, security and technical debt.

growth

AI SEO / GEO in 2026: Your Next Customers Aren’t Humans — They’re Agents

Search is shifting from clicks to answers. Bots and AI agents crawl, cite, recommend, and increasingly buy. Learn what AI SEO / GEO means, why classic SEO is no longer enough, and how PAS7 Studio helps brands win visibility in the agentic web.

blogs

The most powerful Apple chip yet? M5 Pro and M5 Max are breaking records

A data-backed March 2026 analysis of Apple M5 Pro and M5 Max. We break down why these chips can credibly be called Apple's most powerful pro laptop silicon, how they compare with M4 Pro, M4 Max, M1 Pro, M1 Max, and how they stack up against Intel and AMD laptop rivals.

Professional development for your business

We create modern web solutions and bots for businesses. Learn how we can help you achieve your goals.