Skip to main content

Joystick Handlers

Handlers are modular units that define where and how a log should be delivered,
whether to a console, file, or third-party service.

They let a single logger send logs to multiple destinations simultaneously, managed smoothly behind the scenes.

💡 Handlers receive the raw log payload and process it asynchronously, enabling efficient, non-blocking log delivery.

You can add or remove handlers dynamically at runtime::

logger.addHandler(handler, id?, position?); // Adds a handler, returns the assigned ID
logger.removeHandler(id); // Removes the handler by ID

Want to see Handlers in action?
Check out the ✨ Handlers & Integrations for practical code samples and integration tips.

Sparkles Creating Custom Handlers with BaseHandler

You can create custom handlers from scratch by implementing your own handle() method,
or extend Logry’s built-in BaseHandler to simplify the process.

🧱 BaseHandler provides core functionalities such as payload normalization, formatting, and JSON serialization,
plus a safe execution flow, so you only need to focus on implementing the actual log delivery logic.

The key method to implement is:

abstract handle(rawPayload: RawPayload): Promise<void>;

Here are some useful protected methods you can use inside your custom handler:

MethodSignatureDescription
normalize(rawPayload: RawPayload) => NormalizedPayloadNormalize the raw log payload into a consistent format.
format(normalized: NormalizedPayload) => FormattedPayloadFormat the normalized payload into a human-readable or styled format.
toJson(rawPayload: RawPayload, options?: { useNormalizer?: boolean; space?: number }) => stringConvert the raw payload into a JSON string, optionally normalized and pretty-printed.

Example implementation:

import { NodeHandler } from "logry/handlers"; // 📦 Use built-in base handlers from the "logry/handlers" module.

class MyCustomHandler extends BaseHandler {
async handle(rawPayload: RawPayload) {
const normalized = this.normalize(rawPayload);
const formatted = this.format(normalized);
const message = `${formatted.level} | ${formatted.message}`;
// Or for JSON output: const message = this.toJson(rawPayload)
await sendToExternalService(message);
}
}

logger.addHandler(new MyCustomHandler()); // Register the custom handler

Sparkles Platform-Specific Handlers

For more advanced scenarios, you can extend platform-specific base classes such as:

  • NodeHandler
  • BrowserHandler
  • EdgeHandler

These classes build upon BaseHandler, and additionally expose a platform-optimized compose() method that helps you generate the final log message string based on your formatter config and platform constraints.

Example implementation:

import { NodeHandler } from "logry/handlers"; // 📦 Use built-in base handlers from the "logry/handlers" module.

class MyCustomHandler extends NodeHandler {
async handle(rawPayload: RawPayload) {
const message = await this.compose(payload); // Async only in Node.js to append pid and hostname
await sendToExternalService(message);
}
}

This makes it easy to build reliable and composable handlers,
whether you write files, send to remote servers, or push logs to cloud ingestion pipelines ☁️