Skip to content

@json-express/logger-console ​

The default ILogger shipped with @json-express/boot.

@json-express/logger-console implements ILogger using native console.log, console.warn, and console.error. It is auto-discovered by the json-express runtime and registered first, before any other plugin — every other plugin's constructor receives this logger via its option bag.

If you want structured JSON output for production, swap to @json-express/logger-pino.

Installation ​

Most users get this through @json-express/boot. Install directly only when picking the runtime piece-by-piece:

bash
npm install @json-express/logger-console

How the runtime uses it ​

The runtime instantiates the logger with the config provider and then hands the instance to every other plugin:

typescript
// internal — for reference only
const logger = new ConsoleLogger({ configProvider });
kernel.registerLogger(logger);

// every subsequent plugin receives it
new SomePlugin({ configProvider, logger });

Because the logger is registered before any other plugin, every plugin constructor in the JSONExpress ecosystem requires a logger parameter (since commit 8aad8ce, @json-express/core no longer ships a built-in fallback). When authoring a custom plugin, expect the runtime to pass:

typescript
constructor({ configProvider, logger }: { configProvider?: IConfigProvider; logger: ILogger }) {
    this.logger = logger.child({ component: 'MyPlugin' });
}

You generally do not instantiate ConsoleLogger yourself.

Output format ​

[INFO] GET /albums 200 (12ms) {"traceId":"a1b2-c3d4","component":"Express"}
  • Level prefix ([INFO], [WARN], [ERROR])
  • Message
  • JSON-serialised context, including any auto-injected traceId from RequestContext

Core features ​

1. Automatic trace correlation ​

Every log line reads the current traceId from RequestContext (an AsyncLocalStorage wrapper in core) and merges it into the structured context. A single request's logs share the same traceId across plugin boundaries — even when the work hops through queues, hooks, or nested adapters.

2. Child loggers ​

logger.child(extraContext) returns a new logger that inherits the parent's context and adds its own. This is how plugins tag their lines without coordinating on a global format:

typescript
const dbLogger = logger.child({ component: 'Adapter-Memory' });
dbLogger.info('Data loaded');
// [INFO] Data loaded {"component":"Adapter-Memory"}

3. Context-loss diagnostics ​

In complex async code it's easy to accidentally drop the AsyncLocalStorage context (for example by wrapping a callback in an untracked Promise). Set JEX_DEBUG_CONTEXT=true and the logger flags any log line that originates inside a known component but without a traceId:

bash
JEX_DEBUG_CONTEXT=true npx json-express
# [⚠️ CONTEXT LOST] [INFO] some message {"component":"Adapter-Memory"}

This makes context leaks trivial to spot during development. Leave the flag off in production.

4. Silent debug level ​

The debug() method is a deliberate no-op so verbose plugin internals do not clutter terminal output. If you need debug-level logs, install @json-express/logger-pino instead — it honours the jex.log.level config.