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

@wai-industries/polylogger

v1.0.4

Published

SDK for sending logs, audits, and API access events to PolyLogs service

Readme

@wai-industries/polylogger

SDK for sending logs, audits, and API access events to the PolyLogs service.

Installation

npm install @wai-industries/polylogger

Environment Variables

  • POLY_LOGS_SERVICE_URL - URL of the poly-logs service (required if not passed via options)
  • JWT_SECRET - Secret key for signing JWT tokens (required)
  • SERVICE_NAME - Name of your service (default: poly-service)
  • POLY_LOGS_TIMEOUT_MS - Request timeout in milliseconds (default: 2000)
  • LOG_LEVEL - Winston log level (default: info)
  • LOGGER_FORMAT - Logger format: json or default colorized (default: colorized)

Quick Start

1. Trace ID Middleware (Add First)

Add trace ID middleware early in your Express app to ensure trace IDs are available:

import express from 'express';
import { traceMiddleware } from '@wai-industries/polylogger';

const app = express();

// Add trace middleware first
app.use(traceMiddleware);

// ... rest of your middleware

2. API Access Middleware

Automatically log all API requests:

import { createApiAccessMiddleware } from '@wai-industries/polylogger';

// Add after trace middleware
app.use(createApiAccessMiddleware({
  serviceName: 'my-service',
  // Either set POLY_LOGS_SERVICE_URL in env or pass polyLogsUrl here
  // polyLogsUrl: 'http://poly-logs.my-domain:5200',
  // Optional: Only needed if requests don't include organization-id or organization-code in headers
  // If your API sends 'organization-id' or 'organization-code' in request headers, you don't need this
  // organizationCode: 'ORG123',
}));

3. Log Middleware (Optional)

Automatically log request/response events:

import { createLogMiddleware } from '@wai-industries/polylogger';

app.use(createLogMiddleware({
  serviceName: 'my-service',
  // Either set POLY_LOGS_SERVICE_URL in env or pass polyLogsUrl here
  // polyLogsUrl: 'http://poly-logs.my-domain:5200',
  // Optional: Only needed if requests don't include organization-id or organization-code in headers
  // If your API sends 'organization-id' or 'organization-code' in request headers, you don't need this
  // organizationCode: 'ORG123',
}));

4. Winston Logger (Manual Logging)

Note: For automatic request logging with route/method/status, use createLogMiddleware (see step 3) instead of manual logger functions. Manual loggers are useful for custom logging outside of HTTP requests.

Use winston logger that forwards to poly-logs:

import { logInfo, logError, logWarn, logDebug } from '@wai-industries/polylogger';

// Basic usage - logs to console AND forwards to poly-logs (fire-and-forget, non-blocking)
void logInfo('User logged in', { userId: '123', organizationCode: 'ORG-456' });
void logError('Failed to process request', { error: 'Connection timeout', organizationCode: 'ORG-456' });
void logWarn('Rate limit approaching', { current: 90, limit: 100, organizationCode: 'ORG-456' });
void logDebug('Debug information', { data: { /* ... */ }, organizationCode: 'ORG-456' });

// With route/method/status (useful for custom logging or background jobs)
void logInfo('Custom operation completed', {
  organizationCode: 'ORG-456',
  route: '/api/custom-endpoint',
  method: 'POST',
  status: 200,
  userId: 'user-123',
  customField: 'value'
});

void logError('Background job failed', {
  organizationCode: 'ORG-456',
  route: '/jobs/process-payment',
  method: 'POST',
  status: 500,
  error: 'Payment gateway timeout'
});

5. Audit Events (Manual Placement)

Emit audit events where needed in your controllers:

import { emitAudit } from '@wai-industries/polylogger';

// In your controller
export async function createUser(req: Request, res: Response) {
  try {
    const user = await User.create(req.body);
    
    // Emit audit event (fire-and-forget, non-blocking)
    void emitAudit(req, {
      action: 'USER_CREATE',
      entityType: 'user',
      entityId: user._id.toString(),
      result: 'SUCCESS',
      metadata: { email: user.email },
    });
    
    res.json({ data: user });
  } catch (error) {
    // Emit failure audit (fire-and-forget, non-blocking)
    void emitAudit(req, {
      action: 'USER_CREATE',
      entityType: 'user',
      entityId: req.body.id || 'unknown',
      result: 'FAILURE',
      metadata: { error: error.message },
    });
    
    res.status(500).json({ error: 'Failed to create user' });
  }
}

API Reference

Middleware

traceMiddleware

Express middleware that ensures trace ID is present in request.

app.use(traceMiddleware);

createApiAccessMiddleware(options?)

Creates Express middleware to automatically log API access events.

Options:

  • serviceName?: string - Service name (default: SERVICE_NAME env var)
  • organizationCode?: string - Default organization code when request doesn't provide organization-id or organization-code in headers
  • skip?: string[] - Routes to skip (default: /metrics, /favicon.ico, /health, /status)
  • polyLogsUrl?: string - Override poly-logs URL
  • timeoutMs?: number - Request timeout

Note: The middleware automatically extracts organization from request headers (organization-id or organization-code). Only provide organizationCode in options if your requests don't include these headers.

createLogMiddleware(options?)

Creates Express middleware to automatically log request/response events.

Options: Same as createApiAccessMiddleware

Logger Functions

logInfo(message, context?)

logError(message, context?)

logWarn(message, context?)

logDebug(message, context?)

Log messages that are printed to console and forwarded to poly-logs.

⚠️ Important: For automatic request logging with route/method/status, use createLogMiddleware instead. These manual logger functions are useful for custom logging outside of HTTP requests (e.g., background jobs, scheduled tasks, or custom business logic).

Note: These functions are async but designed for fire-and-forget usage. Use void instead of await for non-blocking behavior (recommended):

// ✅ Recommended: Fire-and-forget (non-blocking)
void logInfo('User created', { organizationCode: 'ORG-123' });

// ⚠️ Not recommended: Blocks until HTTP request completes
await logInfo('User created', { organizationCode: 'ORG-123' });

Context:

  • organizationCode: string (required) - Organization code. If neither organizationCode nor orgId is present, the event will NOT be sent to poly-logs.
  • userId?: string - User ID
  • traceId?: string - Trace ID
  • service?: string - Service name
  • jwtToken?: string - JWT token (if available)
  • route?: string - API route (e.g., '/api/users') - useful for filtering in Elasticsearch
  • method?: string - HTTP method (e.g., 'GET', 'POST') - useful for filtering in Elasticsearch
  • status?: number - HTTP status code (e.g., 200, 404, 500) - useful for filtering in Elasticsearch
  • Any other fields will be included in context

Important: Always send organizationCode (or ensure orgId is present via headers/middleware).
If neither is provided, the SDK will log to console only and will drop the event for poly-logs.

Example with route/method/status:

// Custom logging with route/method/status for Elasticsearch filtering
void logInfo('Payment processed', {
  organizationCode: 'ORG-123',
  route: '/api/payments',
  method: 'POST',
  status: 200,
  userId: 'user-456',
  amount: 100.50
});

void logError('Payment failed', {
  organizationCode: 'ORG-123',
  route: '/api/payments',
  method: 'POST',
  status: 500,
  error: 'Insufficient funds'
});

Audit Emitter

emitAudit(req, event, options?)

Emit audit event to poly-logs.

Note: This function is async but designed for fire-and-forget usage. Use void instead of await for non-blocking behavior (recommended):

// ✅ Recommended: Fire-and-forget (non-blocking)
// If request has organization-id or organization-code in headers, it's used automatically
void emitAudit(req, {
  action: 'USER_CREATE',
  entityType: 'user',
  entityId: user._id,
  result: 'SUCCESS',
});

// If request doesn't have organization headers, pass organizationCode in options
void emitAudit(req, {
  action: 'USER_CREATE',
  entityType: 'user',
  entityId: user._id,
  result: 'SUCCESS',
}, {
  organizationCode: 'ORG-123'  // Only needed if headers don't include organization-id or organization-code
});

// ⚠️ Not recommended: Blocks until HTTP request completes
await emitAudit(req, { ... });

Event:

  • action: string - Action name (e.g., USER_CREATE)
  • entityType: string - Entity type (e.g., user)
  • entityId: string - Entity ID
  • result: 'SUCCESS' | 'FAILURE' - Result
  • metadata?: Record<string, unknown> - Additional metadata

Options:

  • organizationCode?: string - Organization code to identify the organization (only needed if request headers don't include organization-id or organization-code)
  • jwtToken?: string - Override JWT token

Note: The function automatically extracts organization from request headers (organization-id or organization-code). Only provide organizationCode in options if your requests don't include these headers.

Helper Functions

getTraceId(req)

Extract trace ID from request.

import { getTraceId } from '@wai-industries/polylogger';

const traceId = getTraceId(req);

Token Handling

The SDK automatically handles JWT tokens:

  1. If token in request header: Uses Authorization: Bearer <token> from request
  2. If no token: Generates service token using JWT_SECRET

Organization Identification

Organizations are identified primarily by organizationCode to avoid exposing internal Mongo _id values, but the SDK supports both for flexibility.

⚠️ Critical: If a log/audit/API access event does not have either organizationCode or orgId, the SDK will skip sending it to poly-logs.
Always ensure organizationCode is available (preferred) for every event.

How It Works

  1. Automatic Detection (Preferred):

    • If your request includes organization-id or organization-code in headers, the SDK uses it automatically
    • No need to pass organizationCode to middleware functions or logger calls
    • This is the recommended approach for inter-service communication
  2. Manual Provision:

    • If requests don't include organization headers, you can pass organizationCode to:
      • Middleware options: createApiAccessMiddleware({ organizationCode: 'ORG-123' })
      • Logger context: logInfo('message', { organizationCode: 'ORG-123' })
      • Audit options: emitAudit(req, event, { organizationCode: 'ORG-123' })
  3. Priority Order:

    • Request header organization-id (MongoDB _id) - works fine if available
    • Request header organization-code (public code) - preferred to avoid exposing MongoDB IDs
    • Function parameter organizationCode - fallback when headers are missing

Best Practices

  • Use headers when possible: If your API gateway or middleware can attach organization-code to all requests, you don't need to pass it manually
  • Avoid exposing MongoDB IDs: Prefer organization-code in headers over organization-id when possible
  • Fallback to function parameters: Only pass organizationCode to functions when headers aren't available

TypeScript Support

Full TypeScript support with exported types:

import type {
  LogEvent,
  AuditEvent,
  ApiAccessEvent,
  SDKConfig,
  MiddlewareOptions,
} from '@wai-industries/polylogger';

License

ISC