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

zulogs

v0.1.4

Published

Official Zulogs Node.js SDK

Readme

Zulogs

Official Node.js SDK for sending logs to the Zulogs API.

Why this SDK

zulogs gives you a small, typed, production-ready client for Zulogs log ingestion with a focus on:

  • strong DX (strict TypeScript types and typed errors)
  • reliability (timeouts, retry with exponential backoff, optional batching)
  • low overhead (no heavy runtime dependencies)

Requirements

  • Node.js >= 18
  • Zulogs API key
  • Zulogs project ID

Install

npm install zulogs

Credentials setup

export ZULOGS_API_KEY="your_zulogs_api_key"
export ZULOGS_PROJECT_ID="your_zulogs_project_id"

Quickstart

import { createZulogsClient } from 'zulogs';

const client = createZulogsClient({
  apiKey: process.env.ZULOGS_API_KEY!,
  projectId: process.env.ZULOGS_PROJECT_ID!,
  context: {
    environment: process.env.NODE_ENV ?? 'development',
    service: 'payments-api',
  },
});

client.log({
  message: 'checkout completed',
  payload: {
    orderId: 'ord_123',
    amount: 4999,
    currency: 'EUR',
  },
});

client.info({
  message: 'checkout completed (shortcut)',
  payload: { orderId: 'ord_123' },
});

await client.shutdown();

API contract (implemented by this SDK)

  • Base URL default: https://api.zulogs.com
  • Endpoint: POST /logs
  • Auth header: Authorization: Bearer <API_KEY>
  • Request payload includes projectId
  • Content type: application/json
  • Success response: HTTP 202 with { accepted: number }
  • Error response body: { error: { code, message, details? } }

Expected behavior with multi-project routing:

  • valid API key + matching projectId in same organization -> success (202)
  • missing projectId in SDK config -> local ZulogsValidationError
  • invalid/deactivated/expired API key -> 401
  • unknown projectId -> backend 404 (or 403, depending on backend mapping)
  • projectId from another organization -> 403

Public API

createZulogsClient(options)

const client = createZulogsClient({
  apiKey: process.env.ZULOGS_API_KEY!,
  projectId: process.env.ZULOGS_PROJECT_ID!,
  baseUrl: 'https://api.zulogs.com',
  timeoutMs: 5_000,
  batchSize: 20,
  flushIntervalMs: 2_000,
  maxRetries: 3,
  retryBaseDelayMs: 200,
  retryMaxDelayMs: 5_000,
  context: {
    environment: process.env.NODE_ENV ?? 'development',
    service: 'worker-ingest',
    tags: {
      region: 'eu-west-1',
    },
  },
  headers: {
    'x-request-source': 'worker-ingest',
  },
  mirrorToConsole: true,
});

createClient(...) remains available as a deprecated alias for backward compatibility.

Client options

| Option | Type | Default | Description | | --- | --- | --- | --- | | apiKey | string | required | Zulogs API key sent as Bearer token. | | projectId | string | required | Zulogs project target for ingestion requests. | | baseUrl | string | https://api.zulogs.com | Override for self-hosted or staging endpoints. | | timeoutMs | number | 5000 | Request timeout in milliseconds. | | batchSize | number | 1 | Queue size threshold before auto-flush. | | flushIntervalMs | number | 0 | Timer-based flush interval (0 = disabled). | | maxRetries | number | 3 | Number of retries after initial attempt for retryable failures. | | retryBaseDelayMs | number | 200 | Base delay used for exponential backoff. | | retryMaxDelayMs | number | 5000 | Upper bound for retry delay. | | context | ZulogsClientContext | {} | Default event fields (for example environment, service, tags) merged into every log. | | headers | Record<string, string> | {} | Extra headers merged into each request. | | includeCodeLocation | boolean | false | Automatically attaches call-site location for log() and single-event ingest(). | | mirrorToConsole | boolean | false | Mirrors all outgoing log events to the local runtime console. | | fetch | custom fetch fn | global fetch | Optional fetch override (testing, instrumentation, custom runtime). |

client.log(event)

Sends one event in fire-and-forget mode.

  • In non-batching mode, it sends immediately.
  • In batching mode, it enqueues until the batch is sent.
  • Returns void (no await required).
  • For short-lived scripts/workers, call client.flush() or client.shutdown() before exit to avoid dropped logs.

client.logAsync(event)

Sends one event and returns { accepted: number }.

  • Use this when you want explicit success/error handling with await/try-catch.

client.trace/debug/info/warn/warning/error/fatal(...)

Typed shortcut methods for the officially supported message types.

  • client.info({ message: '...' }) sets level: 'info'
  • client.warning({ message: '...' }) sets level: 'warning'
  • client.error({ message: '...' }) sets level: 'error'
  • client.error('...') is also supported
  • client.error('...', { payloadKey: 'payloadValue' }) sets payload
  • all shortcuts delegate to client.log(...)
  • Message overload contract: first argument is the required message string; second argument is an optional payload object.

Client Context Defaults

Use context during initialization to avoid repeating environment metadata:

const client = createZulogsClient({
  apiKey: process.env.ZULOGS_API_KEY!,
  projectId: process.env.ZULOGS_PROJECT_ID!,
  context: {
    environment: 'production',
    service: 'api',
    tags: { region: 'eu-west-1' },
  },
});

Merge behavior:

  • context fields are added to every event
  • event-level fields override context fields
  • tags and attributes are shallow-merged (event values win on conflicts)

client.ingest(eventsOrEnvelope)

Supports all accepted request formats:

await client.ingest({ message: 'single event' });
await client.ingest([{ message: 'a' }, { message: 'b' }]);
await client.ingest({ logs: [{ message: 'a' }, { message: 'b' }] });

Behavior notes:

  • single object: can be queued when batching is enabled
  • array and envelope: sent immediately (not queued)
  • SDK always injects projectId into outgoing payloads

client.flush()

Forces all currently queued batch events to be sent immediately.

client.shutdown()

Graceful shutdown method for apps/workers.

  • marks client as closed
  • flushes remaining queued events
  • rejects new logAsync() / ingest() calls afterwards

Log event schema

The SDK validates required and structural fields, but keeps unknown additional fields and forwards them unchanged.

| Field | Type | Required | Notes | | --- | --- | --- | --- | | id | string | no | Optional event id. | | timestamp | string \| number | no | ISO string or Unix sec/ms number. | | level | string | no | Known: trace/debug/info/warn/warning/error/fatal; unknown custom levels are allowed. | | message | string | yes | Required non-empty string. | | payload | Record<string, unknown> | no | Structured payload in addition to message. | | logger | string | no | Logger name. | | service | string | no | Service name. | | environment | string | no | Environment label (prod, staging, ...). | | host | string | no | Hostname/source host. | | source | string | no | Source label (worker, cron, API, etc). | | tags | Record<string, string \| number \| boolean> | no | Indexed tags for filtering/aggregation. | | attributes | Record<string, unknown> | no | Additional structured metadata. | | extra fields | unknown | no | Preserved and transmitted to API. |

Optional: include source code location automatically

If you want each log to include where it was called in your codebase, enable location capture:

const client = createZulogsClient({
  apiKey: process.env.ZULOGS_API_KEY!,
  projectId: process.env.ZULOGS_PROJECT_ID!,
  includeCodeLocation: true,
});

client.log({
  message: 'payment failed',
  payload: { orderId: 'ord_42' },
});

The SDK will add a field like:

{
  "location": {
    "file": "/app/src/payments/handler.ts",
    "line": 84,
    "column": 17,
    "function": "handlePayment"
  }
}

Notes:

  • It uses runtime stack traces, so output format depends on runtime/bundler.
  • Existing event field values are not overwritten. If location already exists, SDK leaves it unchanged.

Optional: mirror logs to local console

If you want each outgoing event to also appear locally, enable console mirroring:

const client = createZulogsClient({
  apiKey: process.env.ZULOGS_API_KEY!,
  projectId: process.env.ZULOGS_PROJECT_ID!,
  mirrorToConsole: true,
});

Console routing is level-aware:

  • trace / debug -> console.debug
  • info -> console.info
  • warn / warning -> console.warn
  • error / fatal -> console.error
  • other or missing levels -> console.log

Validation behavior

The SDK throws ZulogsValidationError before making a network call when:

  • apiKey is missing/empty
  • projectId is missing/empty (projectId is required. Update SDK configuration.)
  • message is missing or empty
  • payload, tags, or attributes have invalid shapes
  • options (timeoutMs, batchSize, etc.) are invalid

Migration note (legacy SDKs)

Legacy SDK configurations that only provide apiKey no longer work.

Update initialization to always include both:

  • apiKey
  • projectId

Error handling

import {
  ZulogsApiError,
  ZulogsNetworkError,
  ZulogsValidationError,
} from '@zulogs/node';

try {
  await client.logAsync({ message: 'hello' });
} catch (error) {
  if (error instanceof ZulogsValidationError) {
    console.error('Validation failed', error.issues);
  } else if (error instanceof ZulogsApiError) {
    console.error('API error', {
      status: error.status,
      code: error.code,
      message: error.message,
      details: error.details,
    });
  } else if (error instanceof ZulogsNetworkError) {
    console.error('Network or timeout issue', error.message);
  } else {
    console.error('Unexpected error', error);
  }
}

Error classes

| Class | Meaning | | --- | --- | | ZulogsValidationError | Invalid client options or event payload, detected locally before request. | | ZulogsNetworkError | Transport-level failure (network issue or timeout). | | ZulogsApiError | Non-202 API response with parsed status, code, and optional details. |

Retry strategy

Retries apply to:

  • HTTP 429
  • HTTP 5xx
  • network/timeout errors (ZulogsNetworkError)

Retry uses exponential backoff with jitter, bounded by retryMaxDelayMs.

Batching strategy

Batch mode is enabled when at least one is true:

  • batchSize > 1
  • flushIntervalMs > 0

Queue flush triggers:

  • queue reaches batchSize
  • flush timer ticks (flushIntervalMs)
  • explicit client.flush()
  • client.shutdown()

Example integration

Publishing checklist

npm run typecheck
npm run test
npm run build

Additional design notes