npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@asimsafar/next-log

v0.1.2

Published

Unified logging for Next.js — server, client, edge middleware. One API, every environment.

Readme

@asimsafar/next-log

Unified logging for Next.js. One API across Server Components, Client Components, and Edge Middleware — with automatic requestId propagation.

npm install @asimsafar/next-log

The Problem

Next.js has no built-in logging solution:

| Environment | Problem | |---|---| | Server Component | console.log works but has no request context | | Client Component | Logs stay in the browser — never reach your server | | Edge Middleware | Context is lost before it reaches Server Components |

@asimsafar/next-log solves all three with a single API.


How It Works

Middleware starts
  → generates requestId + traceId via AsyncLocalStorage
  → context flows automatically into Server Components

Client logs
  → batched and sent to /api/next-log
  → written to server stdout with full context

Output (dev):

10:22:11 INF srv f3a2bc10 [db] query executed {"ms":42}

Output (prod):

{"timestamp":"...","level":"info","msg":"query executed","requestId":"f3a2bc10","module":"db","ms":42}

Setup

1. Create your logger

// lib/logger.ts
import { createLogger } from '@asimsafar/next-log'

export const log = createLogger({
  level: 'info', // 'debug' | 'info' | 'warn' | 'error'
})

2. Add the middleware

// middleware.ts
import { withLogging } from '@asimsafar/next-log/middleware'
import { NextResponse } from 'next/server'

export default withLogging(async (req) => {
  return NextResponse.next()
})

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}

3. Add the API route (receives client logs)

// app/api/next-log/route.ts
export { logHandler as POST } from '@asimsafar/next-log/api'

Usage

import { log } from '@/lib/logger'

log.debug('cache miss', { key: 'user:42' })
log.info('order created', { orderId: 'ORD-881', userId: 'usr-44' })
log.warn('rate limit approaching', { remaining: 10 })
log.error('payment failed', { error: err.message })

// Child logger — scoped context added to every log
const dbLog = log.child({ module: 'database' })
dbLog.info('query executed', { ms: 42 })
// → { level: "info", msg: "query executed", module: "database", requestId: "...", ms: 42 }

Output

Development (pretty format):

10:22:11 INF srv f3a2bc10 order created {"orderId":"ORD-881"}
10:22:11 INF cli f3a2bc10 [database] query executed {"ms":42}
10:22:11 ERR srv f3a2bc10 payment failed {"error":"Insufficient funds"}

Production (JSON format):

{"timestamp":"2026-03-03T10:22:11.432Z","level":"info","msg":"order created","env":"server","requestId":"f3a2bc10","traceId":"span-447","orderId":"ORD-881"}

Configuration

createLogger({
  // Minimum log level (default: 'info')
  level: 'debug',

  // Output format (default: auto — 'pretty' in dev, 'json' in prod)
  format: 'json',

  // Default fields added to every log entry
  defaultMeta: {
    app: 'my-app',
    version: '1.0.0',
  },

  // Override transports per environment
  transports: {
    server: ['console', 'file'], // stdout + logs/app.log
    client: ['http'],            // POST to /api/next-log
    edge:   ['console'],         // stdout
  },

  // Custom endpoint for client logs (default: '/api/next-log')
  logEndpoint: '/api/logs',
})

Transports

| Transport | Environments | Description | |---|---|---| | console | server, edge | Writes to stdout/stderr | | http | client | Batches logs and POSTs to /api/next-log | | file | server | Appends to logs/app.log |


Advanced

Custom transport

import { createLogger, type Transport, type LogEntry } from '@asimsafar/next-log'

const datadogTransport: Transport = {
  write(entry: LogEntry) {
    fetch('https://http-intake.logs.datadoghq.com/api/v2/logs', {
      method: 'POST',
      headers: { 'DD-API-KEY': process.env.DD_API_KEY! },
      body: JSON.stringify(entry),
    })
  }
}

export const log = createLogger()
log.addTransport(datadogTransport)

Manual context

import { setContext } from '@asimsafar/next-log'

// Add fields to the current async context
setContext({ userId: session.user.id, plan: 'pro' })

// All subsequent logs in this request will include these fields
log.info('dashboard loaded')
// → { ..., userId: "usr-44", plan: "pro" }

Middleware options

export default withLogging(handler, {
  requestIdHeader: 'x-request-id',
  traceIdHeader:   'x-trace-id',
  defaultFields:   { region: 'eu-west-1' },
})

Log Entry Shape

{
  timestamp:  string                               // ISO 8601
  level:      'debug' | 'info' | 'warn' | 'error'
  msg:        string
  env:        'server' | 'client' | 'edge'
  requestId?: string                               // set by withLogging()
  traceId?:   string                               // set by withLogging()
  // ...any additional fields you pass
}

License

MIT