# Cordis Logger Plugin Integration with Events System: A Complete Technical Guide

> Integrate the Cordis logger plugin with the events system by registering exporters that listen to the LoggerService. Learn how the EventsService enables dynamic exporter registration at runtime in this technical guide.

- Repository: [Cordiverse/cordis](https://github.com/cordiverse/cordis)
- Tags: technical-guide
- Published: 2026-08-25

---

**The Cordis logger plugin integrates with the events system by registering exporters that listen to the `LoggerService`, while the core `EventsService` handles internal plugin lifecycle hooks like `internal/plugin` to enable dynamic exporter registration at runtime.**

Cordis ships with a built-in **logger service** exposed on every `Context` instance as `ctx.logger`. This service works hand-in-hand with the **events subsystem** to emit log messages as regular events, allowing plugins—including the console logger—to listen, filter by level, and format output dynamically. This article examines the source code in the `cordiverse/cordis` repository to explain exactly how the logger plugin integrates with Cordis's event-driven architecture.

## Core Logger Implementation in [`packages/core/src/logger.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/logger.ts)

The foundation of Cordis logging resides in **[`packages/core/src/logger.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/logger.ts)**, which defines the `Logger` class, the `LoggerService` callable, and the static helper methods used by the events system.

`LoggerService` is instantiated inside the `Context` constructor and registers a default exporter that buffers messages. Each `Logger` instance (created via `ctx.logger(name)`) stores its own name, level, and metadata, then forwards every logging call (`error`, `info`, `warn`, `debug`) to the internal **`_method`** implementation.

Inside `_method`, the logger constructs a **message object** (`Message`) and dispatches it to every registered exporter that accepts the current log level:

```typescript
// packages/core/src/logger.ts – core dispatch logic
export class Logger {
  private _method(type: LoggerType, level: number): LoggerMethod {
    return (...args) => {
      const sn = ++this.service._snMessage
      const ts = Date.now()
      for (const exporter of this.service.exporters.values()) {
        const targetLevel = exporter.levels?.[this.name] ?? exporter.levels?.default ?? this.level ?? LoggerLevel.INFO
        if (targetLevel < level) continue
        const message: Message = { sn, ts, type, level, name: this.name, ...this.meta, args }
        exporter.export(message)               // <-- integration point with events system
      }
    }
  }
}

```

Exporters are simple objects implementing an `export(message)` function. While the default exporter only buffers messages, specialized plugins like the console logger implement custom exporters that interact with the broader events ecosystem.

## Events System Architecture in [`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts)

The events engine is defined in **[`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts)**, providing methods like `emit`, `parallel`, `serial`, `bail`, and `waterfall`. While the logger does not call the events engine directly for every log line, the integration occurs through internal lifecycle hooks.

The logger service registers listeners for the **`internal/plugin`** event (handled within [`packages/core/src/context.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/context.ts)), allowing plugins to react when new services are registered. This enables hot-module-replacement (HMR) and dynamic exporter registration:

```typescript
// packages/core/src/events.ts – internal listener registration
this.on('internal/listener', function (this: Context, name, listener, options) {
  if (name === 'internal/update' && !options.global) {
    const hooks = this.fiber._hooks['internal/update'] ??= new DisposableList()
    const method = options.prepend ? 'unshift' : 'push'
    return hooks[method](listener)
  }
})

```

When a plugin (such as `@cordis/logger-console`) loads via `ctx.loader`, the events system triggers the `internal/plugin` hook, giving the logger plugin an opportunity to attach its exporter to the `LoggerService`.

## Console Logger Plugin Implementation

The concrete console implementation lives in **[`packages/logger-console/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/logger-console/src/index.ts)** and **[`packages/logger-console/src/shared.ts`](https://github.com/cordiverse/cordis/blob/main/packages/logger-console/src/shared.ts)**. The `ConsoleExporter` extends the abstract base exporter, adding Node.js `util.inspect` formatters (`%o` and `%O`) and color support detection:

```typescript
// packages/logger-console/src/index.ts – console exporter implementation
export class ConsoleExporter extends Base {
  formatters: Record<string, Formatter> = {
    o: inspectFormatter,
    O: inspectFormatter,
  }

  getDefaults() {
    return {
      ...super.getDefaults(),
      colors: (supportsColor.stdout ? supportsColor.stdout.level : 0) as false | 0 | 1 | 2 | 3,
    }
  }
}

```

When the plugin loads, it calls `ctx.logger.exporter(new ConsoleExporter())`. The exporter's `export` method receives every `Message` produced by the core logger, applies formatting and color codes via `Logger.color`, and writes to `stdout`.

## Step-by-Step Integration Flow

The integration between the Cordis logger plugin and events system follows this sequence:

1. **Context Creation** – `Context` instantiates `LoggerService` and attaches it to the context prototype as `ctx.logger`.
2. **Plugin Loading** – When `@cordis/logger-console` installs, its `setup` function registers a `ConsoleExporter` via `ctx.logger.exporter()`.
3. **Event Hook Registration** – The events system emits `internal/plugin`, allowing the logger to finalize exporter setup and register for `internal/update` hooks if needed.
4. **Logging Call** – `ctx.logger('my-plugin').info('message')` creates a `Message` object and iterates through all registered exporters.
5. **Message Export** – Each exporter's `export(message)` method receives the message. The console exporter formats and prints it, while custom exporters might emit additional events or write to external systems.

## Practical Implementation Examples

Here is a complete example demonstrating how to initialize the Cordis logger plugin, create named loggers, and implement custom exporters that leverage the events system:

```typescript
import { createApp } from 'cordis'
import ConsoleLogger from '@cordis/logger-console'

const app = createApp()

// Install the console logger plugin (registers exporter via events system)
app.plugin(ConsoleLogger)

// Create a named logger instance
const log = app.logger('my-plugin')

// Log at different levels with formatting
log.info('Server started at %s', new Date().toLocaleTimeString())
log.warn('Low memory: %d MB left', process.memoryUsage().heapTotal / 1e6)
log.error(new Error('Something went wrong'))

// Dynamically add a custom exporter (e.g., for file logging or external services)
app.logger.exporter({
  colors: false,
  export(message) {
    // Custom logic: write JSON to file, send to remote service, etc.
    console.log('FILE:', JSON.stringify(message))
  },
})

```

## Summary

- **[`packages/core/src/logger.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/logger.ts)** contains the `LoggerService` and `Logger` classes that dispatch messages to registered exporters via the `_method` implementation.
- **[`packages/core/src/events.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/events.ts)** provides the event infrastructure used for `internal/plugin` and `internal/update` hooks, enabling dynamic exporter registration.
- **Exporters** act as the bridge between the logger and the events system, implementing the `export(message)` interface to receive log data.
- **[`packages/logger-console/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/logger-console/src/index.ts)** demonstrates a concrete implementation using `ConsoleExporter` with formatting and color support.
- The integration is **runtime-extensible**: plugins can register or remove exporters dynamically through the events system without restarting the application.

## Frequently Asked Questions

### How does the Cordis logger plugin integrate with the events system?

The integration occurs through the `LoggerService` registering exporters that listen to the `internal/plugin` event. When plugins load, the events system triggers these hooks, allowing exporters to attach themselves to the logger. The `LoggerService` then broadcasts log messages to all registered exporters, effectively treating them as event listeners for log data.

### What is the role of exporters in the Cordis logging system?

Exporters are objects implementing an `export(message)` method that receives `Message` objects from the `LoggerService`. They act as the terminal point for log output, handling formatting, filtering by level, and transport (console, file, network). The default exporter buffers messages, while plugins like `@cordis/logger-console` provide specialized exporters that write to specific destinations.

### How do I create a custom logger exporter in Cordis?

Create an object with an `export(message)` method and pass it to `ctx.logger.exporter()`. The method receives a `Message` object containing `sn` (sequence number), `ts` (timestamp), `type`, `level`, `name`, and `args`. You can optionally include a `levels` property to control filtering per logger name. For example, you could implement a file exporter that writes JSON lines or a webhook exporter that POSTs to an external service.

### Where is the LoggerService instantiated in the Cordis codebase?

`LoggerService` is instantiated inside the `Context` constructor located in **[`packages/core/src/context.ts`](https://github.com/cordiverse/cordis/blob/main/packages/core/src/context.ts)**. It is immediately attached to the context prototype as `ctx.logger`, making it available to all plugins and application code within that context scope. The service maintains a registry of exporters and a message sequence counter used for ordering log entries.