Skip to content
Logixlysia
Esc
↑↓navigate↵open⌘Jpreview
On this page

Adapters Overview

Ship logs to observability platforms with built-in adapters

Send your logs to external observability platforms with built-in adapters. Each adapter is a regular transport — batched, retried, and non-blocking — so you can mix them with console and file logging or run them exclusively.

Available Adapters

Cloud Platforms

Platform Import Best for
Axiom logixlysia/axiom Schema-free log analytics — every field is queryable
Better Stack logixlysia/better-stack Logs, uptime, and alerting in one place
Datadog logixlysia/datadog Enterprise observability with facets and pipelines
Sentry logixlysia/sentry Structured logs next to your errors and traces
PostHog logixlysia/posthog Product analytics — link logs to persons and funnels

Self-Hosted & Open Standards

Platform Import Best for
OTLP logixlysia/otlp Any OpenTelemetry backend — collectors, Grafana Cloud, New Relic, Honeycomb, SigNoz
HyperDX logixlysia/hyperdx Open-source observability via OTLP
Grafana Loki logixlysia/loki Label-indexed logs for the Grafana stack
ClickHouse logixlysia/clickhouse Your own SQL log warehouse, no pipeline in between

Quick Start

Set the platform’s environment variables, create the transport, and pass it to transports:

import { Elysia } from 'elysia'
import logixlysia from 'logixlysia'
import { createAxiomTransport } from 'logixlysia/axiom'

const app = new Elysia()
  .use(
    logixlysia({
      config: {
        transports: [createAxiomTransport()]
      }
    })
  )
  .get('/', () => 'ok')
  .listen(3000)

Trigger a request and the access log appears in your platform’s log explorer.

Shared Behavior

All adapters share the same core:

  • Batching — entries buffer and flush either when maxBatchSize is reached (default 20) or after flushIntervalMs (default 2000 ms), whichever comes first.
  • Retries — network errors, 429, and 5xx responses retry with linear backoff (default 2 retries). Other 4xx responses fail immediately.
  • Timeout — each request aborts after timeout ms (default 5000).
  • Non-blocking — sends run in the background and never delay your HTTP responses. Failures are reported through onError (sink 'transport') or rate-limited to stderr.
  • Credentials — read from environment variables by default; options passed to the factory always win. Missing credentials throw at startup with an actionable message, not silently at runtime.
  • Endpoint safety — endpoint URLs are validated when the transport is created and must use http: or https:, and requests never follow redirects, so a credential header can’t be forwarded to another origin.

Every adapter accepts these options on top of its platform-specific ones:

Option Type Default Description
maxBatchSize number 20 Entries buffered before an immediate flush
flushIntervalMs number 2000 Max time an entry waits before the buffer is sent
timeout number 5000 Per-request timeout in milliseconds
retries number 2 Retry attempts on network errors, 429, and 5xx
maxPendingBatches number 32 Batches allowed to be waiting on the backend at once
onError (error: unknown) => void — Called when a batch fails after retries or is dropped

Batches are sent one at a time, so they arrive in the order they were logged and a slow backend applies backpressure instead of piling up requests. Once maxPendingBatches batches are waiting, new batches are dropped rather than buffered forever. Pass the same function you give config.onError as the adapter’s onError to see every batch failure — including the ones from the interval timer, which no caller is awaiting — in one place.

Multiple Destinations

Adapters compose — fan the same logs out to several platforms:

import { createAxiomTransport } from 'logixlysia/axiom'
import { createSentryTransport } from 'logixlysia/sentry'

app.use(
  logixlysia({
    config: {
      transports: [createAxiomTransport(), createSentryTransport()]
    }
  })
)

Production-Only External Logging

Use useTransportsOnly to disable console and file output and send logs exclusively to your platform:

app.use(
  logixlysia({
    config: {
      transports: [createAxiomTransport()],
      useTransportsOnly: process.env.NODE_ENV === 'production'
    }
  })
)

Graceful Shutdown

When the app stops, the plugin starts flushing every transport and the file sink. Elysia’s app.stop() does not wait for plugin stop hooks, so a process that exits right after await app.stop() can cut that flush short. Before you exit, await flushLogixlysia with the same options object you passed to the plugin. The stop hook’s own wait is bounded by flushTimeoutMs (5000 ms by default; 0 starts the flush without waiting), and a flush that runs out of time is reported through onError with sink: 'shutdown'.

What that does not cover:

  • SIGKILL, or a crash — nothing runs.
  • A process that exits without calling app.stop(), including a bare SIGINT with no handler.
  • A serverless function frozen between invocations.

Signals are yours to handle, because a library that installs its own handlers fights the rest of your process. This handler stops the app, waits for the flush, then exits:

import { Elysia } from 'elysia'
import logixlysia, { flushLogixlysia } from 'logixlysia'
import { createAxiomTransport } from 'logixlysia/axiom'

const options = { config: { transports: [createAxiomTransport()] } }
const app = new Elysia().use(logixlysia(options)).listen(3000)

process.on('SIGTERM', async () => {
  await app.stop()
  await flushLogixlysia(options)
  process.exit(0)
})

For workers, scripts, and anything else that logs outside the Elysia lifecycle, drain the sinks directly:

import { flushLogixlysia } from 'logixlysia'

await flushLogixlysia(options, { close: true })

close: true also releases sockets, timers, and the file handle; omit it to flush and keep logging.

What Gets Sent

Each log carries its level, message, and the full meta object: the request method and URL, the response status, durationMs, and everything merged into the request context — request IDs, trace IDs, user IDs, and your own fields. Platforms that prefer flat attributes (HyperDX, Sentry, PostHog) receive dot-notation keys like request.method and context.requestId; Axiom receives the nested structure as-is.

Redaction runs before transports, so autoRedact and redactKeys apply to everything an adapter ships off-box.

Last updated on October 3, 2026

Was this page helpful?