UXDL Docs

Axiom

Structured log ingestion, querying, and dashboards.

Axiom is our log management platform. It ingests structured logs and events, lets you query them with APL (Axiom Processing Language), and build dashboards for monitoring and incident investigation.

What Axiom provides

FeaturePurpose
Log ingestionCollect logs from apps, servers, and OTel collectors
APL queriesSearch and aggregate logs with a SQL-like language
DashboardsVisualize error rates, latency, and custom metrics
MonitorsAlert when query results cross a threshold
Long retentionStore logs cost-effectively without managing infrastructure

How Axiom fits our stack

Structured logging setup

Use structured JSON logs — never plain console.log in production code.

typescript
// lib/logger.ts
type LogLevel = "debug" | "info" | "warn" | "error";
 
interface LogEvent {
  level: LogLevel;
  message: string;
  requestId?: string;
  userId?: string;
  service: string;
  [key: string]: unknown;
}
 
export function log(event: LogEvent) {
  console.log(
    JSON.stringify({
      ...event,
      timestamp: new Date().toISOString(),
      service: event.service ?? process.env.OTEL_SERVICE_NAME,
    }),
  );
}
typescript
log({
  level: "info",
  message: "Order created",
  requestId: req.headers["x-request-id"],
  orderId: order.id,
  userId: user.id,
});

Shipping logs to Axiom

Option 1 — Direct HTTP ingest

bash
pnpm add @axiomhq/js
typescript
import { Axiom } from "@axiomhq/js";
 
const axiom = new Axiom({ token: process.env.AXIOM_TOKEN! });
 
async function shipLog(event: LogEvent) {
  await axiom.ingest(process.env.AXIOM_DATASET!, [event]);
}

Option 2 — OpenTelemetry log exporter

Configure OTel to export logs to Axiom's OTLP endpoint:

dotenv
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://api.axiom.co/v1/logs
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ${AXIOM_TOKEN},X-Axiom-Dataset=${AXIOM_DATASET}

Option 3 — Vercel log drain

For Next.js on Vercel, configure a log drain pointing to Axiom's ingest URL.

Environment variables

dotenv
AXIOM_TOKEN=xaat-...              # Ingest token — vault only
AXIOM_DATASET=logs-prod           # Dataset name per environment
AXIOM_ORG_ID=company              # Organization identifier
EnvironmentDataset
Alphalogs-alpha
Betalogs-beta
Productionlogs-prod

Querying with APL

Find errors for a specific request:

apl
['logs-prod']
| where level == "error"
| where requestId == "req_01HZ..."
| project timestamp, message, service, _raw
| sort by timestamp desc

Error rate over the last hour:

apl
['logs-prod']
| where timestamp > ago(1h)
| summarize errorCount = countif(level == "error") by bin(timestamp, 5m)
| sort by timestamp asc

Monitors and alerts

Create monitors in the Axiom UI for:

  • Error rate > 5% over 5 minutes → #engineering Slack
  • level == "error" count > 50 in 1 minute → PagerDuty
  • Missing health check logs → SRE alert

Incident investigation workflow

  1. Start from Sentry issue — copy the requestId.
  2. Query Axiom: where requestId == "..." to see full request log trail.
  3. Check OpenTelemetry trace for the same ID in Sentry Performance.
  4. Correlate timestamps across all three tools.

Official documentation