Agent skill
axiom
Implements structured logging and observability with Axiom for serverless applications. Use when adding logging, tracing, and Web Vitals monitoring to Next.js and Vercel applications.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/axiom
SKILL.md
Axiom
Observability platform for logs, metrics, and traces. Native Vercel integration with structured logging for Next.js applications.
Quick Start
bash
npm install @axiomhq/js @axiomhq/logging @axiomhq/nextjs @axiomhq/react
Next.js Setup (2025 API)
Create Axiom Client
typescript
// lib/axiom/client.ts
import { Axiom } from '@axiomhq/js';
export const axiom = new Axiom({
token: process.env.AXIOM_TOKEN!,
orgId: process.env.AXIOM_ORG_ID,
});
Create Logger
typescript
// lib/axiom/logger.ts
import { Logger, ConsoleTransport, AxiomJSTransport } from '@axiomhq/logging';
import { nextJsFormatters } from '@axiomhq/nextjs';
import { axiom } from './client';
export const logger = new Logger({
transports: [
new AxiomJSTransport({
axiom,
dataset: process.env.AXIOM_DATASET!,
}),
new ConsoleTransport(),
],
formatters: nextJsFormatters,
});
Create Route Handler
typescript
// lib/axiom/route.ts
import { createAxiomRouteHandler } from '@axiomhq/nextjs';
import { axiom } from './client';
import { logger } from './logger';
export const axiomRouteHandler = createAxiomRouteHandler({
axiom,
logger,
dataset: process.env.AXIOM_DATASET!,
});
API Route Handler
typescript
// app/api/axiom/route.ts
import { axiomRouteHandler } from '@/lib/axiom/route';
export const POST = axiomRouteHandler;
Logging
Server Components
typescript
// app/page.tsx
import { logger } from '@/lib/axiom/logger';
export default async function Page() {
logger.info('Page rendered', { page: 'home' });
try {
const data = await fetchData();
logger.debug('Data fetched', { count: data.length });
return <div>{/* render */}</div>;
} catch (error) {
logger.error('Failed to fetch data', { error });
throw error;
} finally {
await logger.flush();
}
}
API Routes
typescript
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { logger } from '@/lib/axiom/logger';
export async function POST(request: NextRequest) {
const body = await request.json();
logger.info('Creating user', {
email: body.email,
source: request.headers.get('referer'),
});
try {
const user = await createUser(body);
logger.info('User created', { userId: user.id });
return NextResponse.json(user);
} catch (error) {
logger.error('User creation failed', {
error: error instanceof Error ? error.message : 'Unknown error',
email: body.email,
});
return NextResponse.json({ error: 'Failed' }, { status: 500 });
} finally {
await logger.flush();
}
}
Middleware
typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { logger } from '@/lib/axiom/logger';
export async function middleware(request: NextRequest) {
const start = Date.now();
logger.info('Request started', {
path: request.nextUrl.pathname,
method: request.method,
});
const response = NextResponse.next();
logger.info('Request completed', {
path: request.nextUrl.pathname,
duration: Date.now() - start,
});
await logger.flush();
return response;
}
Client-Side Logging
tsx
// app/providers.tsx
'use client';
import { AxiomWebVitals } from '@axiomhq/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<>
<AxiomWebVitals />
{children}
</>
);
}
Client Logger (Proxy Method)
Send logs through your API to avoid exposing tokens:
typescript
// lib/axiom/client-logger.ts
class ClientLogger {
private queue: Array<{ level: string; message: string; data?: object }> = [];
log(level: string, message: string, data?: object) {
this.queue.push({ level, message, data });
}
info(message: string, data?: object) {
this.log('info', message, data);
}
error(message: string, data?: object) {
this.log('error', message, data);
}
async flush() {
if (this.queue.length === 0) return;
const logs = [...this.queue];
this.queue = [];
await fetch('/api/axiom', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ logs }),
});
}
}
export const clientLogger = new ClientLogger();
Client Component Usage
tsx
'use client';
import { useEffect } from 'react';
import { clientLogger } from '@/lib/axiom/client-logger';
export function TrackableButton() {
const handleClick = () => {
clientLogger.info('Button clicked', { buttonId: 'cta' });
clientLogger.flush();
};
useEffect(() => {
clientLogger.info('Component mounted');
return () => {
clientLogger.info('Component unmounted');
clientLogger.flush();
};
}, []);
return <button onClick={handleClick}>Click me</button>;
}
Web Vitals
Automatic Core Web Vitals tracking:
tsx
// app/layout.tsx
import { AxiomWebVitals } from '@axiomhq/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<AxiomWebVitals />
{children}
</body>
</html>
);
}
Legacy API (next-axiom)
The older next-axiom package is still supported:
bash
npm install next-axiom
Setup
typescript
// next.config.js
const { withAxiom } = require('next-axiom');
module.exports = withAxiom({
// your next config
});
Server Component Logging
typescript
import { Logger } from 'next-axiom';
export default async function Page() {
const log = new Logger();
log.info('Page rendered');
await log.flush();
return <div>Content</div>;
}
Client Component Logging
tsx
'use client';
import { useLogger } from 'next-axiom';
export function ClientComponent() {
const log = useLogger();
const handleClick = () => {
log.info('Button clicked');
};
return <button onClick={handleClick}>Click</button>;
}
Structured Logging Patterns
Request Context
typescript
function createRequestLogger(request: NextRequest) {
return {
info: (message: string, data?: object) => {
logger.info(message, {
...data,
requestId: request.headers.get('x-request-id'),
path: request.nextUrl.pathname,
method: request.method,
userAgent: request.headers.get('user-agent'),
});
},
error: (message: string, data?: object) => {
logger.error(message, {
...data,
requestId: request.headers.get('x-request-id'),
path: request.nextUrl.pathname,
});
},
};
}
Timing
typescript
async function withTiming<T>(
name: string,
fn: () => Promise<T>
): Promise<T> {
const start = Date.now();
try {
const result = await fn();
logger.info(`${name} completed`, {
duration: Date.now() - start,
success: true,
});
return result;
} catch (error) {
logger.error(`${name} failed`, {
duration: Date.now() - start,
error: error instanceof Error ? error.message : 'Unknown',
});
throw error;
}
}
// Usage
const users = await withTiming('fetchUsers', () => db.user.findMany());
Error Logging
typescript
function logError(error: unknown, context?: object) {
if (error instanceof Error) {
logger.error(error.message, {
...context,
name: error.name,
stack: error.stack,
cause: error.cause,
});
} else {
logger.error('Unknown error', {
...context,
error: String(error),
});
}
}
Vercel Integration
Enable the Axiom integration in Vercel for automatic log drains:
- Go to Vercel Dashboard > Integrations
- Install Axiom integration
- Connect your Axiom organization
- Logs from
console.log,console.error, etc. are automatically sent
Environment Variables
bash
AXIOM_TOKEN=xaat-xxxxxxxx
AXIOM_ORG_ID=your-org-id
AXIOM_DATASET=your-dataset
# For next-axiom
NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT=https://api.axiom.co/v1/datasets/your-dataset/ingest
Query Examples (APL)
// Recent errors
dataset
| where level == "error"
| order by _time desc
| limit 100
// Request latency by path
dataset
| where message == "Request completed"
| summarize avg(duration), p95(duration), count() by path
| order by count_ desc
// Errors by type
dataset
| where level == "error"
| summarize count() by ['error.name']
| order by count_ desc
Best Practices
- Flush before returning - Always call flush() in server components
- Use structured data - Pass objects, not strings
- Add request context - Include requestId, path, method
- Log at appropriate levels - debug, info, warn, error
- Proxy client logs - Don't expose tokens to browser
- Use Vercel integration - For automatic console.log capture
- Add timing - Track duration for performance monitoring
Didn't find tool you were looking for?