NNO 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 to stdout/stderr, compatible with Cloudflare's log aggregation pipeline.

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

Log Levels

LevelMethodOutput stream
"debug"logger.debug()stdout
"info"logger.info()stdout
"warn"logger.warn()stdout
"error"logger.error()stderr

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