Abrova
☰ Docs menu · Next.js SDK logging

Next.js SDK Logging

Full-stack TypeScript logging for Next.js applications with App Router and Pages Router support.

Last updated: 27 Oct 2026

Full-stack TypeScript logging for Next.js applications with App Router and Pages Router support.

Installation#

Add the Abrova registry to .npmrc, then install (see Next.js SDK):

bash
# .npmrc
@abrova-trace:registry=https://center.abrova.ir/api/packages/abrova/npm/
bash
npm install @abrova-trace/nextjs-sdk

Initialize Logger#

Create lib/logger.ts:

typescript
import { LogService } from '@abrova-trace/nextjs-sdk';

export const logger = new LogService({
  apiKey: process.env.ABROVA_TRACE_API_KEY!,
  sourceId: 'nextjs-app',
  sourceName: 'My Next.js App',
  platform: 'nextjs',
  environment: process.env.NODE_ENV,
});

App Router (Next.js 13+)#

typescript
// app/api/users/route.ts
import { NextResponse } from 'next/server';
import { logger } from '@/lib/logger';

export async function GET(request: Request) {
  logger.log('Fetching users', 'info');

  try {
    const users = await db.users.findMany();
    logger.log('Users fetched', 'info', { count: users.length });
    return NextResponse.json(users);
  } catch (error) {
    logger.log('Failed to fetch users', 'error', {
      error: error instanceof Error ? error.message : 'Unknown',
    });
    return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
  }
}

Pages Router (API Routes)#

typescript
// pages/api/orders.ts
import { logger } from '@/lib/logger';

export default async function handler(req, res) {
  logger.log('Order request received', 'info', {
    method: req.method,
    path: req.url,
  });

  try {
    const order = await createOrder(req.body);
    logger.log('Order created', 'info', { orderId: order.id });
    res.json(order);
  } catch (error) {
    logger.log('Order creation failed', 'error', {
      error: error.message,
    });
    res.status(500).json({ error: 'Failed to create order' });
  }
}

Log Levels#

typescript
// LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'

logger.log('Detailed trace info', 'trace');
logger.log('Debug information', 'debug');
logger.log('User action completed', 'info');
logger.log('Approaching rate limit', 'warn');
logger.log('Failed to save data', 'error');
logger.log('App crashed unexpectedly', 'fatal');

Middleware Integration#

typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { logger } from '@/lib/logger';

export function middleware(request: NextRequest) {
  const start = Date.now();

  logger.log('Request received', 'info', {
    method: request.method,
    path: request.nextUrl.pathname,
  });

  const response = NextResponse.next();
  const duration = Date.now() - start;

  logger.log('Request processed', 'info', {
    path: request.nextUrl.pathname,
    duration,
  });

  return response;
}

export const config = { matcher: '/api/:path*' };

Client-Side Logging#

Two options. In the browser you can log straight through the SDK instance you already initialized, e.g. AbrovaTrace.getInstance()?.info('Button clicked', { buttonId: 'submit-form' }). If you would rather validate or filter client logs on your own server first, send them through an API route as below. Logs are buffered (sent after 50 logs or 30 seconds by default) and are not sent automatically when the page closes, so call AbrovaTrace.getInstance()?.flushLogs() when a log must go out right away.

Create a proxy route:

typescript
// app/api/log/route.ts
import { NextResponse } from 'next/server';
import { logger } from '@/lib/logger';

export async function POST(request: Request) {
  const { message, level, metadata } = await request.json();

  const validLevels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
  const safeLevel = validLevels.includes(level) ? level : 'info';

  logger.log(message, safeLevel, { ...metadata, source: 'client' });

  return NextResponse.json({ success: true });
}

Client component:

typescript
'use client';

async function logToServer(message: string, level: string, metadata?: object) {
  await fetch('/api/log', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message, level, metadata }),
  });
}

// Usage
logToServer('Button clicked', 'info', { buttonId: 'submit-form' });

Configuration#

OptionTypeRequiredDescription
apiKeystringYesYour project API key
sourceIdstringNoLog source identifier. With it, logs are sent in batches (/api/logs/ingest/batch); without it, each buffered log is sent in its own request
sourceNamestringNoDisplay name for this source
platformstringYesPlatform identifier, e.g. "nextjs" (no default)
environmentstringYesEnvironment name, e.g. "production" (no default)
releasestringNoApp version attached to each log
batchSizenumberNoFlush at this buffer size (default: 50)
flushIntervalMsnumberNoMax ms before flush (default: 30000)
maxBufferSizenumberNoDrop oldest when exceeded (default: 1000)
debugbooleanNoPrint SDK debug output to the console (default: false)
apiUrlstringNoBase URL of the Trace API (default: "https://trace.abrova.ir"). Set it only for self-hosted

Best Practices#

Centralize Logger

Create a single logger instance in lib/logger.ts and import it everywhere

Filter Client Logs

Use a proxy route when you want to validate or filter client logs before they reach Abrova Trace

Log API Routes

Add logging to all API routes for full request visibility

Add Request Context

Include method, path, and duration in request logs

Validate Client Logs

Sanitize and validate log level/message from client-side proxy

Use Middleware

Add middleware for automatic request/response logging on API routes

Was this page helpful?
Report a problem