How to Enable Structured JSON Logging in workerd: Complete Configuration Guide

Enable structured JSON logging in workerd by setting structuredLogging = true in the logging block of your workerd.capnp configuration file.

workerd is Cloudflare's open-source JavaScript/WebAssembly runtime. When you activate structured JSON logging, the runtime replaces the default human-readable console output with a stream of newline-delimited JSON objects based on the Cap'n Proto schema log_schema::LogEntry.

How Structured Logging Works in workerd

The structured logging system intercepts all log calls at the process level and converts them to JSON before writing to STDOUT or STDERR.

The Cap'n Proto Foundation

The logging schema is defined in src/workerd/server/log-schema.capnp and imported by the JSON logger implementation. This schema enforces a consistent structure for every log entry, including timestamp, severity level, source location, and message content.

Process Context Activation

When the server parses your configuration, it checks for the structuredLogging flag in src/workerd/server/workerd.c++ (lines 24-28). If enabled, the code calls StructuredLoggingProcessContext::enableStructuredLogging(), which creates a JsonLogger instance that implements kj::ExceptionCallback. This overrides the standard KJ_LOG macro and exception handling pathways, ensuring all logs flow through the JSON pipeline instead of the default kj::TopLevelProcessContext.

Configuration Steps

Minimal Configuration Example

Add the structuredLogging field to the logging struct in your workerd.capnp file:


# workerd-config.capnp

using Workerd = import "/workerd/workerd.capnp";

const config :Workerd.Config = (
  logging = (
    structuredLogging = true,
  ),
  
  services = [(
    name = "main",
    worker = (modules = [(name = "worker", esModule = embed "worker.js")]),
  )],
);

The logging struct definition lives in src/workerd/server/workerd.capnp (lines 99-104).

Optional Prefix Configuration

You can customize output prefixes for stdout and stderr streams:

const config :Workerd.Config = (
  logging = (
    structuredLogging = true,
    stdoutPrefix = "stdout: ",
    stderrPrefix = "stderr: ",
  ),
);

Logging Behavior and Output Format

JSON Schema and Fields

In structured mode, StructuredLoggingProcessContext builds each log entry using buildJsonLogMessage (defined in src/workerd/server/json-logger.c++, lines 34-57). The resulting JSON includes:

  • timestamp: Unix timestamp in milliseconds
  • level: Severity level (INFO, WARNING, ERROR, etc.)
  • source: Source file and line number (e.g., "worker.cpp:123")
  • message: The log content
  • contextDepth: Optional nesting level for contextual logging

Example output:

{"timestamp":1700345678901,"level":"WARNING","source":"worker.cpp:123","message":"Cache miss","contextDepth":0}

C++ Logging with KJ_LOG

When structured logging is active, all KJ_LOG macro calls are intercepted by JsonLogger::logMessage (see json-logger.c++ line 59):

#include <kj/debug.h>

void initializeWorker() {
  KJ_LOG(INFO, "Worker startup complete");
  KJ_LOG(WARNING, "Cache miss for key 'user:42'");
  KJ_LOG(ERROR, "Database connection failed");
}

Each call emits a single JSON line to the output stream without buffering.

JavaScript Console Logging

Worker-side code automatically benefits from structured logging. The runtime forwards console.* methods through the same JsonLogger pipeline:

console.log("Processing request");     // -> {"level":"INFO","message":"Processing request",...}
console.warn("Rate limit approaching"); // -> {"level":"WARNING","message":"Rate limit approaching",...}
console.error("Unhandled exception");  // -> {"level":"ERROR","message":"Unhandled exception",...}

Capturing and Parsing Logs

Pipe the newline-delimited JSON stream to tools like jq for filtering and formatting:

workerd --config workerd-config.capnp | jq -r '. | select(.level == "ERROR") | .message'

This command filters for error-level messages and extracts the human-readable content. The behavior is verified by the test suite in src/workerd/server/json-logger-test.c++, which exercises both default and structured modes beginning at line 45.

Summary

  • Configuration: Set structuredLogging = true in the logging block of workerd.capnp (defined in src/workerd/server/workerd.capnp).
  • Activation: The flag triggers StructuredLoggingProcessContext::enableStructuredLogging() in workerd.c++, creating a JsonLogger instance.
  • Format: Logs become newline-delimited JSON objects with timestamp, level, source, message, and contextDepth fields.
  • Coverage: Both C++ KJ_LOG macros and JavaScript console.* methods route through the structured pipeline.
  • Implementation: The core logic resides in src/workerd/server/json-logger.c++ using the Cap'n Proto schema from log-schema.capnp.

Frequently Asked Questions

How do I enable structured JSON logging in workerd?

Set structuredLogging = true inside the logging struct of your workerd.capnp configuration file. When the server starts, it detects this flag and calls StructuredLoggingProcessContext::enableStructuredLogging() to activate the JSON logger instead of the default plain-text output.

What fields are included in workerd's structured log output?

Every JSON log entry contains timestamp (Unix milliseconds), level (severity string), source (file and line), message (log content), and contextDepth (nesting level). The buildJsonLogMessage function in src/workerd/server/json-logger.c++ constructs this format according to the Cap'n Proto log_schema::LogEntry definition.

Does structured logging affect JavaScript console methods?

Yes. When structured logging is enabled, console.log, console.warn, and console.error calls from worker code are automatically converted to JSON format by the same JsonLogger pipeline that handles C++ logs. You do not need to modify your JavaScript code to benefit from structured output.

Where is the structured logging schema defined?

The schema is defined in src/workerd/server/log-schema.capnp and implemented in src/workerd/server/json-logger.c++. The configuration options are declared in src/workerd/server/workerd.capnp (lines 99-104), while the runtime activation logic appears in src/workerd/server/workerd.c++.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →