Skip to main content

Logging

Logging is provided by the dedicated @carno.js/logger package. The core package stays small, while the logger package provides a structured LoggerService that can be registered through the same plugin and dependency injection system used by the rest of Carno.

Use logging for operational visibility:

  • Request and background job flow.
  • Important business events.
  • Integration failures.
  • Debug information during development.
  • Startup and shutdown diagnostics.

Installation

bun add @carno.js/logger

Registering the Logger

Use the ready-made CarnoLogger plugin when the default configuration is enough.

import { Carno } from '@carno.js/core';
import { CarnoLogger } from '@carno.js/logger';

const app = new Carno()
.use(CarnoLogger);

app.listen(3000);

This registers LoggerService in the DI container.

Injecting LoggerService

import { Service } from '@carno.js/core';
import { LoggerService } from '@carno.js/logger';

@Service()
class UserService {
constructor(private logger: LoggerService) {}

createUser(email: string) {
this.logger.info('Creating user', { email });

try {
// create user
this.logger.debug('User created successfully', { email });
} catch (error) {
this.logger.error('Failed to create user', {
email,
error: error instanceof Error ? error.message : String(error),
});
}
}
}

The logger accepts a message and optional structured data.

Log Levels

LoggerService supports these levels:

LevelUse for
DEBUGDetailed diagnostics for development and troubleshooting
INFONormal operational events
WARNRecoverable problems or suspicious conditions
ERRORFailed operations that need attention
FATALCritical failures; flushes immediately
SILENTDisable output

Custom Configuration

Use createCarnoLogger(config) to create a configured logger plugin.

import { Carno } from '@carno.js/core';
import { createCarnoLogger, LogLevel } from '@carno.js/logger';

const LoggerModule = createCarnoLogger({
level: LogLevel.DEBUG,
pretty: process.env.NODE_ENV !== 'production',
timestamp: true,
prefix: 'api',
flushInterval: 10,
});

const app = new Carno()
.use(LoggerModule);

Configuration options:

OptionDescription
levelMinimum log level to output
prettyPretty-print structured data
timestampInclude timestamp in each line
timestampFormatCustom timestamp formatter
prefixPrefix added to every log line
flushIntervalBuffer flush interval in milliseconds. 0 writes synchronously

Standalone Logger

Use createLogger() when you need a logger outside a Carno app.

import { createLogger, LogLevel } from '@carno.js/logger';

const logger = createLogger({
level: LogLevel.INFO,
prefix: 'worker',
});

logger.info('Worker started');

Buffering and Shutdown

By default, LoggerService buffers log lines briefly and flushes them on an interval. This reduces write overhead in hot paths.

The service also listens for application shutdown and process exit to flush remaining logs.

Call logger.close() manually only when you create a standalone logger and control its lifecycle yourself.

Practical Guidelines

  • Log events that help operate the system, not every internal branch.
  • Put high-cardinality details in structured data instead of the message.
  • Avoid logging secrets, tokens, passwords or full request bodies.
  • Use debug for noisy development details and raise the level in production.
  • Use request IDs from middleware when correlating logs across services.

See Also