Neutrino Docs
SDK Reference

@neutrino-io/logger

Structured JSON logger for Neutrino Cloudflare Workers services.

Package: @neutrino-io/logger

Lightweight structured logging for Cloudflare Workers services. Emits newline-delimited JSON via console.log() (info/warn/debug) and console.error() (error), compatible with Cloudflare's log aggregation pipeline (surfaces in CF Workers logs).

import {
  Logger,
  createLogger,
  initTrace,
  withTraceHeaders,
  requestLogger,
} from "@neutrino-io/logger";

Log Levels

LevelMethodOutput
"debug"logger.debug()console.log()
"info"logger.info()console.log()
"warn"logger.warn()console.log()
"error"logger.error()console.error()

Logger

Structured JSON logger class. Each instance is bound to a service name and optionally to a traceId and requestId for request-scoped logging.

class Logger {
  constructor(service: string, traceId?: string, requestId?: string);

  info(message: string, data?: Record<string, unknown>): void;
  warn(message: string, data?: Record<string, unknown>): void;
  error(message: string, data?: Record<string, unknown>): void;
  debug(message: string, data?: Record<string, unknown>): void;
}

Every call emits a LogEntry JSON object:

interface LogEntry {
  timestamp: string; // ISO 8601
  level: LogLevel;
  service: string;
  traceId?: string;
  requestId?: string;
  message: string;
  [key: string]: unknown; // spread from the data argument
}

Example output:

{
  "timestamp": "2026-03-31T12:00:00.000Z",
  "level": "info",
  "service": "iam",
  "traceId": "abc-123",
  "requestId": "req-456",
  "message": "→ request",
  "method": "POST",
  "path": "/api/nno/session"
}

createLogger(service)

Factory function — creates a Logger instance bound to the given service name. Use this for module-level loggers that are not yet bound to a request context.

function createLogger(service: string): Logger;
import { createLogger } from "@neutrino-io/logger";

const log = createLogger("my-service");

log.info("Service started");
log.error("Unhandled error", { error: err.message });

For request-scoped logging with trace IDs, prefer constructing a Logger directly or using the requestLogger Hono middleware.


initTrace(request)

Extracts traceId and requestId from incoming request headers (x-trace-id, x-request-id). Generates new UUIDs for any missing values.

function initTrace(request: Request): { traceId: string; requestId: string };
import { initTrace, Logger } from "@neutrino-io/logger";

export default {
  async fetch(request: Request, env: Env) {
    const { traceId, requestId } = initTrace(request);
    const log = new Logger("my-worker", traceId, requestId);
    log.info("Handling request");
  },
};

withTraceHeaders(headers, traceId, requestId)

Adds x-trace-id and x-request-id headers to an existing headers object. Returns a new Headers instance — does not mutate the input.

Use this when making outbound fetch calls to downstream services to propagate the trace context.

function withTraceHeaders(
  headers: Headers | [string, string][] | Record<string, string> | undefined,
  traceId: string,
  requestId: string,
): Headers;
import { initTrace, withTraceHeaders } from "@neutrino-io/logger";

const { traceId, requestId } = initTrace(request);

const response = await fetch("https://registry.svc.nno.app/api/platforms", {
  headers: withTraceHeaders(
    { Authorization: `Bearer ${token}` },
    traceId,
    requestId,
  ),
});

requestLogger(service) — Hono Middleware

Hono middleware that handles the full per-request logging lifecycle. On each request it:

  1. Calls initTrace to extract or generate traceId and requestId
  2. Stores both on the Hono context (c.get('traceId'), c.get('requestId'))
  3. Creates a request-scoped Logger and stores it on the context (c.get('logger'))
  4. Logs an incoming request line (→ request)
  5. Awaits next(), then logs the outgoing response line (← response) with status and duration
import type { MiddlewareHandler } from "hono";

function requestLogger(service: string): MiddlewareHandler;
import { Hono } from "hono";
import { requestLogger } from "@neutrino-io/logger";

const app = new Hono();

app.use("*", requestLogger("iam"));

app.get("/health", (c) => {
  const log = c.get("logger") as Logger;
  log.info("Health check");
  return c.json({ ok: true });
});

Context values set by this middleware:

KeyTypeDescription
traceIdstringDistributed trace ID (from header or generated)
requestIdstringPer-request ID (from header or generated)
loggerLoggerRequest-scoped logger bound to traceId and requestId

Log output example:

{"timestamp":"2026-03-31T12:00:00.000Z","level":"info","service":"iam","traceId":"abc","requestId":"req-1","message":"→ request","method":"GET","path":"/health"}
{"timestamp":"2026-03-31T12:00:00.050Z","level":"info","service":"iam","traceId":"abc","requestId":"req-1","message":"← response","method":"GET","path":"/health","status":200,"duration":50}

On this page