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
maxBatchSizeis reached (default 20) or afterflushIntervalMs(default 2000 ms), whichever comes first. - Retries — network errors,
429, and5xxresponses retry with linear backoff (default 2 retries). Other4xxresponses fail immediately. - Timeout — each request aborts after
timeoutms (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.
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 |
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
Batching timers never keep the process alive, so flush pending entries before exit. beforeExit alone does not fire on SIGTERM/SIGINT, so handle those too:
const axiom = createAxiomTransport()
const FLUSH_DEADLINE_MS = 3000
const shutdown = async () => {
await app.stop()
await Promise.race([
axiom.flush().catch(() => {
/* already reported */
}),
new Promise(resolve => setTimeout(resolve, FLUSH_DEADLINE_MS))
])
process.exit(0)
}
process.on('SIGTERM', shutdown)
process.on('SIGINT', shutdown)
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.