afterlog

Adapters

Send logs anywhere with custom adapters

Adapters connect afterlog to your logging infrastructure. afterlog doesn't send logs anywhere by default - you provide the adapter.

Console Adapter

Built-in adapter for development:

import { createConsoleAdapter } from "afterlog"

const adapter = createConsoleAdapter({
  method: "log", // console method: log, info, warn, error, debug
  pretty: false, // pretty print JSON
  includeTimestamp: false,
})

afterlog.configure({ adapter })

Custom Adapter

Implement the LoggerAdapter interface:

const myAdapter = {
  async emit(event) {
    await fetch("https://logs.example.com/v1/input", {
      method: "POST",
      body: JSON.stringify(event),
    })
  },
}

Interface

interface LoggerAdapter<T extends BaseWideEvent> {
  emit(event: Finalized<T>): void | Promise<void>

  flush?(): Promise<void>

  destroy?(): Promise<void>

  isHealthy?(): boolean | Promise<boolean>
}

Only emit is required.

Datadog Adapter

const datadogAdapter = {
  async emit(event) {
    await fetch("https://http-intake.logs.datadoghq.com/v1/input", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "DD-API-KEY": process.env.DD_API_KEY,
      },
      body: JSON.stringify({
        ...event,
        ddsource: "nodejs",
        service: process.env.SERVICE_NAME,
      }),
    })
  },
}

File Adapter

import fs from "fs"

const fileAdapter = {
  stream: fs.createWriteStream("/var/log/app.jsonl", { flags: "a" }),

  emit(event) {
    return new Promise((resolve, reject) => {
      this.stream.write(JSON.stringify(event) + "\n", (err) => {
        if (err) reject(err)
        else resolve()
      })
    })
  },

  flush() {
    return new Promise((resolve) => {
      this.stream.end(resolve)
    })
  },
}

Lifecycle Methods

flush()

Called during graceful shutdown:

adapter: {
  async flush() {
    await this.buffer.flush()
  }
}

destroy()

Called when afterlog instance is destroyed:

adapter: {
  async destroy() {
    await this.client.close()
  }
}

isHealthy()

Health check for monitoring:

adapter: {
  async isHealthy() {
    return this.client.isConnected()
  }
}

Metrics

AdapterManager tracks metrics automatically:

const metrics = afterlog.getMetrics()
// {
//   eventsEmitted: 1000,
//   eventsFailed: 2,
//   bytesEmitted: 50000,
//   lastEmitTime: Date,
//   lastErrorTime: Date | null,
//   averageEmitDurationMs: 5.2
// }

Hooks

Add hooks via AdapterManager:

const manager = afterlog.getAdapterManager()

manager.onEmit((event) => {
  console.log("Event emitted:", event.request_id)
})

manager.onError((error, event) => {
  console.error("Emit failed:", error.message)
})

Environment-Based Config

const isProd = process.env.NODE_ENV === "production"

afterlog.configure({
  adapter: isProd ? datadogAdapter : createConsoleAdapter({ pretty: true }),

  service: process.env.SERVICE_NAME,

  sampling: {
    defaultRate: isProd ? 0.05 : 1.0,
  },
})

On this page