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

vestig

v0.22.0

Published

Leave a trace. A modern, runtime-agnostic structured logging library with automatic PII sanitization and context propagation.

Readme

👣 Vestig

Leave a trace.

A modern, runtime-agnostic structured logging library with automatic PII sanitization and context propagation.

CI npm version Tests Coverage License: MIT TypeScript PRs Welcome


Why Vestig?

Vestig — from Latin vestigium (trace, footprint). Leave a trace of what happened.

| Feature | Vestig | Pino | Winston | |---------|:-----:|:----:|:-------:| | Runtime Agnostic | ✅ | ❌ | ❌ | | Auto PII Sanitization | ✅ | ❌ | ❌ | | GDPR/HIPAA/PCI-DSS Presets | ✅ | ❌ | ❌ | | Wide Events / Tail Sampling | ✅ | ❌ | ❌ | | Zero Config | ✅ | ✅ | ❌ | | TypeScript First | ✅ | ✅ | ⚠️ | | Edge Runtime Support | ✅ | ❌ | ❌ | | Browser Support | ✅ | ❌ | ⚠️ | | Context Propagation | ✅ | ❌ | ❌ | | Multiple Transports | ✅ | ✅ | ✅ | | Zero Dependencies | ✅ | ❌ | ❌ |

Vestig is the only logging library that:

  • Works everywhere (Node.js, Bun, Deno, Edge, Browser)
  • Automatically sanitizes PII with compliance presets
  • Propagates context through async operations
  • Has zero runtime dependencies

Installation

# bun
bun add vestig

# npm
npm install vestig

# pnpm
pnpm add vestig

Quick Start

import { log } from 'vestig'

// Simple logging
log.info('Hello world')
log.error('Something failed', { userId: 123 })

// Sensitive data is automatically redacted
log.info('User login', {
  email: '[email protected]',     // → us***@example.com
  password: 'secret123',          // → [REDACTED]
  creditCard: '4111111111111111', // → ****1111
})

Features

Multi-Transport Support

Send logs to multiple destinations simultaneously:

import { createLogger, HTTPTransport, DatadogTransport, SentryTransport } from 'vestig'

const log = createLogger()

// Add HTTP transport for centralized logging
log.addTransport(new HTTPTransport({
  name: 'api-logs',
  url: 'https://logs.example.com/ingest',
  headers: { 'Authorization': 'Bearer token' },
}))

// Add Datadog for observability
log.addTransport(new DatadogTransport({
  name: 'datadog',
  apiKey: process.env.DD_API_KEY,
  service: 'my-app',
  tags: ['env:production'],
}))

// Add Sentry for error tracking
log.addTransport(new SentryTransport({
  name: 'sentry',
  dsn: process.env.SENTRY_DSN,
  environment: 'production',
  release: '[email protected]',
  minLevel: 'warn', // Only send warn/error to Sentry
}))

// Initialize transports (starts flush timers)
await log.init()

// All logs go to console, HTTP endpoint, Datadog, and Sentry
log.info('Server started', { port: 3000 })

Available Transports

| Transport | Description | Use Case | |-----------|-------------|----------| | ConsoleTransport | Console output with colors | Development, debugging | | HTTPTransport | Send to any HTTP endpoint | Custom log aggregation | | FileTransport | Write to files with rotation | Server-side logging | | DatadogTransport | Datadog Log Management | Production observability | | SentryTransport | Sentry error tracking | Error monitoring, alerting |

PII Sanitization with Presets

Choose from compliance-focused presets:

import { Sanitizer } from 'vestig'

// GDPR compliance (EU data protection)
const gdprSanitizer = Sanitizer.fromPreset('gdpr')

// HIPAA compliance (healthcare)
const hipaaSanitizer = Sanitizer.fromPreset('hipaa')

// PCI-DSS compliance (payment cards)
const pciSanitizer = Sanitizer.fromPreset('pci-dss')

// Apply to logger
const log = createLogger({
  sanitize: 'gdpr', // Use preset name directly
})

| Preset | Fields Protected | Patterns Applied | |--------|-----------------|------------------| | none | None | None | | minimal | password, secret, token, key | None | | default | 26 common fields | Email, Credit Card, JWT | | gdpr | + name, address, phone, IP | + IP addresses, phone | | hipaa | + patient, medical, SSN | + SSN pattern | | pci-dss | + card, CVV, PIN | Full card detection |

Custom Sanitization

import { Sanitizer } from 'vestig'

const sanitizer = new Sanitizer({
  fields: [
    'customSecret',
    { type: 'prefix', value: 'private_' },
    { type: 'contains', value: 'token' },
  ],
  patterns: [{
    name: 'internal-id',
    pattern: /ID-[A-Z0-9]+/g,
    replacement: '[ID_REDACTED]',
  }],
})

const safe = sanitizer.sanitize({
  private_key: 'abc123',     // → [REDACTED]
  auth_token: 'xyz789',      // → [REDACTED]
  internalId: 'ID-ABC123',   // → [ID_REDACTED]
})

Child Loggers

const log = createLogger({ namespace: 'app' })
const dbLog = log.child('database')
const cacheLog = log.child('cache')

dbLog.info('Query executed')  // [app:database] Query executed
cacheLog.info('Cache hit')    // [app:cache] Cache hit

Context & Correlation IDs

import { withContext, createCorrelationContext } from 'vestig'

// Next.js API Route
export async function GET(req: Request) {
  const context = createCorrelationContext({
    requestId: req.headers.get('x-request-id') ?? undefined
  })

  return withContext(context, async () => {
    log.info('Request started')
    // All logs include: requestId, traceId, spanId

    const result = await fetchData()
    log.info('Request completed')

    return Response.json(result)
  })
}

Wide Events (Canonical Log Lines)

Wide Events capture all context about a complete operation in ONE structured event. Instead of scattered logs, you get a single comprehensive record per request.

import { createLogger, createWideEvent } from 'vestig'

const log = createLogger({
  tailSampling: {
    enabled: true,
    alwaysKeepStatuses: ['error'],  // 100% of errors
    slowThresholdMs: 2000,          // 100% of slow requests
    successSampleRate: 0.1,         // 10% of successful requests
    vipUserIds: ['user-123'],       // 100% for VIP users
  }
})

// Create and enrich the wide event throughout the request
const event = createWideEvent({ type: 'http.request' })

// Add HTTP context
event.merge('http', {
  method: 'POST',
  path: '/api/checkout',
  status_code: 200,
})

// Add user context
event.merge('user', {
  id: 'user-456',
  subscription: 'premium',
})

// Add performance metrics
event.merge('performance', {
  db_query_ms: 45,
  external_api_ms: 230,
})

// End and emit the event
const completedEvent = event.end({ status: 'success' })
log.emitWideEvent(completedEvent)

// Output: ONE event with 50+ fields, easily queryable

Why Wide Events?

  • Debug faster: All context in one place, no log correlation needed
  • Reduce costs: Tail sampling keeps 100% of errors, samples success
  • Better queries: "Show me slow requests from premium users with payment errors"
  • No missing context: You'll never again lose the request that caused an error

Configuration

Environment Variables

VESTIG_LEVEL=debug        # trace | debug | info | warn | error
VESTIG_ENABLED=true       # Enable/disable logging
VESTIG_STRUCTURED=true    # JSON output (auto-enabled in production)
VESTIG_SANITIZE=true      # PII sanitization (default: true)

# Add to context
VESTIG_CONTEXT_SERVICE=api
VESTIG_CONTEXT_VERSION=1.0.0

Programmatic

const log = createLogger({
  level: 'debug',
  enabled: true,
  structured: false,
  sanitize: 'gdpr',  // or true, false, or SanitizeConfig
  context: { environment: 'development' }
})

Log Levels

| Level | Description | |-------|-------------| | trace | Very detailed debugging information | | debug | Development debugging | | info | General information | | warn | Warning messages | | error | Error messages (includes stack traces) |

Runtime Detection

Vestig automatically detects and adapts to:

  • Node.js - Full features with AsyncLocalStorage
  • Bun - Full features with AsyncLocalStorage
  • Deno - Full features with AsyncLocalStorage (via node:async_hooks)
  • Edge Runtime - Vercel Edge, Cloudflare Workers
  • Browser - Client-side logging (use with @vestig/next for best experience)
import { RUNTIME, IS_SERVER, IS_DENO } from 'vestig'

console.log(RUNTIME) // 'node' | 'bun' | 'deno' | 'edge' | 'browser' | 'worker' | 'unknown'

Auto-Production Mode

In production (NODE_ENV=production), Vestig automatically:

  • Sets log level to warn
  • Enables structured (JSON) output
  • Keeps sanitization enabled

API Reference

createLogger(config?)

Create a new logger instance.

log.trace/debug/info/warn/error(message, metadata?)

Log at the specified level.

log.child(namespace, config?)

Create a namespaced child logger.

log.addTransport(transport)

Add a transport to the logger.

log.removeTransport(name)

Remove a transport by name.

log.flush()

Flush all buffered logs.

log.destroy()

Cleanup all transports (call on shutdown).

withContext(context, fn)

Run a function with the given context.

createCorrelationContext(existing?)

Generate correlation IDs (requestId, traceId, spanId).

Sanitizer.fromPreset(preset)

Create a sanitizer from a preset name.

createWideEvent(config)

Create a wide event builder for accumulating context throughout a request.

const event = createWideEvent({ type: 'http.request' })
event.set('http', 'method', 'POST')
event.merge('user', { id: 'user-123', tier: 'premium' })
const completed = event.end({ status: 'success' })

log.emitWideEvent(event)

Emit a completed wide event through the logger's transports. Applies tail sampling if configured.

createTailSampler(config)

Create a tail sampler for wide events. Used internally by emitWideEvent() but can be used standalone.

const sampler = createTailSampler({
  alwaysKeepStatuses: ['error'],
  slowThresholdMs: 2000,
  successSampleRate: 0.1,
})
if (sampler.shouldSample(event).sampled) {
  log.emitWideEvent(event)
}

Contributing

We love contributions! Please read our Contributing Guide to get started.

License

MIT © Arakiss

See LICENSE for more details.