Skip to content

Logging

The SDK uses Pino for high-performance logging with structured JSON output. By default, logs are written to stderr to comply with the MCP protocol expectations.

typescript

import { createLogger } from "@contextvm/sdk/core";
// Create a logger for your module
const logger = createLogger("my-module");
logger.info("Application started");
logger.error("An error occurred", { error: "details" });

typescript

import { createLogger, LoggerConfig } from "@contextvm/sdk/core";
const config: LoggerConfig = {
level: "debug", // Minimum log level (debug, info, warn, error)
file: "app.log", // Optional: log to a file instead of stderr
};
const logger = createLogger("my-module", config);

Note: Pretty printing is automatically enabled when logs are written to stderr/stdout (not to a file) for better readability during development.

The logger can be configured using environment variables, which is useful for adjusting log output without changing the code.

  • LOG_LEVEL: Sets the minimum log level.
    • Values: debug, info, warn, error.
    • Default: info.
  • LOG_DESTINATION: Sets the log output destination.
    • Values: stderr (default), stdout, or file.
  • LOG_FILE: Specifies the file path when LOG_DESTINATION is file.
  • LOG_ENABLED: Enables or disables logging.
    • Values: true (default) or false.

bash

Terminal window
# Set log level to debug
LOG_LEVEL=debug node app.js
# Log to a file instead of the console
LOG_DESTINATION=file LOG_FILE=./app.log node app.js
# Disable logging entirely
LOG_ENABLED=false node app.js

javascript

// Set this in a <script> tag in your HTML or at the top of your entry point
window.LOG_LEVEL = "debug";
// Now, when you import and use the SDK, it will use the 'debug' log level.
import { logger } from "@contextvm/sdk";
logger.debug("This is a debug message.");

typescript

const baseLogger = createLogger("my-app");
const authLogger = baseLogger.withModule("auth");
const dbLogger = baseLogger.withModule("database");
authLogger.info("User login attempt");
dbLogger.debug("Query executed", { query: "SELECT * FROM users" });
  • Log levels:
    • debug: detailed developer-oriented information (traces, SQL queries, internal state).
    • info: high-level lifecycle events and successful operations (startup, shutdown, user actions).
    • warn: unexpected situations that don’t stop execution but may need attention.
    • error: failures that require investigation (exceptions, failed requests).
  • Use structured fields to add context instead of human-parsed messages. Preferred field names:
    • module: module or component name (string).
    • event: short event name (string).
    • txId / traceId: request or trace identifier (string).
    • userId: authenticated user id when applicable (string).
    • durationMs: operation duration in milliseconds (number).
    • error: when logging errors, include an error object with message and stack.
  • Avoid logging sensitive data (passwords, secrets, full tokens). Redact or hash when necessary.
  • Keep log messages concise and use structured metadata for details.
  • Prefer logger.method(message, meta) over string interpolation that embeds structured data into the message.

typescript

logger.info("payment.processed", {
module: "payments",
txId: "abcd-1234",
userId: "user-42",
amount: 1999,
currency: "EUR",
durationMs: 245,
});
try {
// do something that throws
} catch (err) {
logger.error("payment.failed", {
module: "payments",
txId: "abcd-1234",
error: { message: err.message, stack: err.stack },
});
}
  • The SDK logger is optimized for minimal allocations. Prefer structured objects over building large strings.
  • When logging high-frequency events, consider using lower log levels (info -> debug) or sampling.
  • Ensure log files are rotated and monitored if using file destinations.

See also: src/content/docs/ts-sdk/core/interfaces.md