# OpenWork Logging: How the Platform Implements Structured Observability with Pino

> Discover how OpenWork logging utilizes Pino for structured observability. Explore its centralized JSON logging with automatic context for better insights.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: deep-dive
- Published: 2026-08-02

---

**OpenWork implements a centralized, structured logging system built on the Pino library, using a single `appLogger` instance that forks into component-specific child loggers to emit JSON-formatted logs with automatic context injection.**

OpenWork relies on high-performance observability to manage its enterprise features, and its logging architecture reflects this priority. The platform implements a **structured logging** strategy centered around the `pino` library, enabling machine-readable JSON output across all services. This approach ensures that logs from different subsystems can be aggregated, filtered, and analyzed without complex parsing.

## Centralized Logger Configuration

The foundation of OpenWork logging resides in [`dev/ee/apps/den-api/src/observability/logger.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/observability/logger.ts). This file exports a singleton instance named `appLogger` that wraps the Pino library with platform-specific configuration.

By instantiating the logger once and importing it across modules, OpenWork maintains consistent log formatting and transport settings throughout the application. The centralization eliminates configuration drift and ensures that every subsystem emits logs with the same baseline properties, including timestamps and log levels.

## Component-Scoped Child Loggers

Rather than creating new logger instances for each module, OpenWork uses **Pino's child logger pattern** to create specialized loggers that inherit from `appLogger` while injecting component-specific metadata.

In [`dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/plugin-system/store.ts), the implementation creates a child logger scoped to the plugin system:

```typescript
import { appLogger } from "../../../observability/logger.js";

const logger = appLogger.child({ component: "plugin_system_store" });

```

Similarly, [`dev/ee/apps/den-api/src/routes/org/mcp-connections.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/routes/org/mcp-connections.ts) initializes a logger for connection management:

```typescript
const logger = appLogger.child({ component: "mcp_connections" });

```

This pattern automatically appends a `component` field to every log entry, enabling precise filtering in log aggregation tools like Loki or Datadog. When `logger.info()` or `logger.error()` is called, the resulting JSON includes both the message and the component identifier.

## Structured JSON Output and Log Levels

OpenWork logging adheres to standard severity levels: **debug**, **info**, **warn**, and **error**. The Pino-based implementation emits these as newline-delimited JSON objects, making the logs immediately consumable by structured logging pipelines.

When an error occurs in the plugin system store, the code captures both the error message and contextual data:

```typescript
logger.error("failed to update marketplace", {
  marketplaceId,
  error: err.message,
});

```

The resulting JSON output includes the `component` field from the child logger, the `marketplaceId`, and the error details, eliminating the need for string concatenation or manual formatting. This structured approach supports high-cardinality fields without performance penalties, as Pino optimizes JSON serialization through native fast-stringify mechanisms.

## Development-Only Logging Utilities

While production code relies on the structured Pino logger, OpenWork includes lightweight console-style utilities for local development scripts. The file [`dev/scripts/dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/dev/scripts/dev-headless-web.ts) implements a simple `logLine` helper that writes prefixed messages to a temporary log file:

```typescript
const logLine = (message: string) => {
  console.log(`[dev:headless-web] ${message}`);
};

logLine("Starting services");
logLine(`Workspace: ${workspace}`);
logLine(`Web URL: ${webUrl}`);

```

This dual-path approach ensures that developers see human-readable output during local debugging, while production deployments capture machine-readable JSON suitable for automated alerting and analysis.

## Summary

OpenWork implements a sophisticated yet straightforward logging strategy that balances developer experience with production observability:

- **Centralized Pino instance**: The `appLogger` singleton in [`dev/ee/apps/den-api/src/observability/logger.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/apps/den-api/src/observability/logger.ts) provides consistent configuration across all services.
- **Component scoping**: Child loggers inject `component` metadata automatically, enabling granular filtering by subsystem.
- **JSON structured output**: All production logs emit as JSON, supporting direct ingestion into log aggregation platforms.
- **Standard log levels**: The implementation supports `debug`, `info`, `warn`, and `error` methods throughout the codebase.
- **Development utilities**: Simple console wrappers in scripts like [`dev/scripts/dev-headless-web.ts`](https://github.com/different-ai/openwork/blob/main/dev/scripts/dev-headless-web.ts) provide readable output for local debugging without affecting production log structure.

## Frequently Asked Questions

### What logging library does OpenWork use?

OpenWork uses the **Pino** library for its core logging infrastructure. Pino was selected for its high-performance JSON serialization and low overhead, making it suitable for production environments where log volume and parsing speed are critical concerns.

### How does OpenWork distinguish logs from different components?

OpenWork distinguishes logs by creating **child loggers** via `appLogger.child({ component: "component_name" })`. Each child logger automatically includes the component identifier in every log entry, allowing operators to filter logs by specific subsystems such as `plugin_system_store` or `mcp_connections` without parsing message strings.

### What format do OpenWork logs use in production?

In production, OpenWork emits **JSON-formatted log lines**. Each log entry is a newline-delimited JSON object containing the message, severity level, timestamp, component name, and any additional structured metadata passed by the developer. This format integrates directly with modern log aggregation services.

### Does OpenWork support different log levels?

Yes, OpenWork supports standard log levels including **debug**, **info**, **warn**, and **error**. These levels are available on all child loggers and can be filtered at the transport level or within log aggregation tools to reduce noise during routine operations.