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

@ubercode/pino-cloudwatch

v1.0.1

Published

A TypeScript pino v7+ transport for Amazon CloudWatch Logs.

Readme

@ubercode/pino-cloudwatch

npm version CI TypeScript Node.js License: MIT

A modern TypeScript pino v7+ transport for Amazon CloudWatch Logs, built on AWS SDK v3.

This is the actively-maintained successor to the original pino-cloudwatch, rebuilt from the ground up in TypeScript. It replaces the legacy stdin-pipe CLI (AWS SDK v2, sequence-token protocol) with a worker-thread pino-abstract-transport, and inherits the mission-critical batching/throttling/bounded-memory core from its sibling @ubercode/winston-cloudwatch.

Project status. The original pino-cloudwatch is unmaintained — no releases or issue activity in years. @ubercode/pino-cloudwatch is its de-facto continuation: every open upstream issue and PR has been triaged and addressed (see docs/upstream-issue-audit.md), and bug reports and feature requests are tracked in this repository going forward.

Features

  • pino v7+ transport — runs in pino's worker thread; only records emitted through pino are shipped (your console.log is never captured)
  • AWS SDK v3 — modular, tree-shakeable; no sequence-token handshake
  • Bounded memory — a CloudWatch outage can never stall pino or leak memory; the queue is strictly bounded (oldest-dropped)
  • Resilient delivery — rate-limited batches, exponential-backoff retry, and head-of-line drop so a persistent outage never wedges the pipeline
  • Byte-aware batching — respects the 1 MB PutLogEvents payload limit
  • Graceful flush — drains queued logs on shutdown via the transport close hook
  • Flexible formatting — default [LEVEL] message {meta}, optional jsonMessage, or fully custom
  • Dynamic stream names{hostname}/{pid}/{date}/{time} tokens
  • 100% test coverage with Jest, plus a sustained memory soak

Installation

npm install @ubercode/pino-cloudwatch pino
# or: pnpm add @ubercode/pino-cloudwatch pino

Usage

Recommended: worker-thread transport

import pino from 'pino'

const logger = pino({
  transport: {
    target: '@ubercode/pino-cloudwatch',
    options: {
      logGroupName: '/my-app/logs', // REQUIRED
      logStreamName: 'production',   // optional; defaults to "<hostname>-<pid>"
      createLogGroup: true,
      createLogStream: true,
      awsConfig: { region: 'us-east-1' },
    },
  },
})

logger.info({ userId: 123, action: 'login' }, 'Hello CloudWatch!')

Because pino runs the transport in a worker thread, the options object is structured-cloned across the thread boundary, so it must be JSON-serializable. Functions (formatLog, formatLogItem, a credential-provider function) and a pre-built cloudWatchLogs client cannot be passed this way — use the in-process form below for those.

Advanced: in-process (supports functions & custom clients)

pino accepts a destination stream as its second argument. This runs the transport in the main thread, so callbacks and a bring-your-own client work:

import pino from 'pino'
import pinoCloudWatch from '@ubercode/pino-cloudwatch'

const stream = pinoCloudWatch({
  logGroupName: '/my-app/logs',
  logStreamName: 'production',
  formatLog: item => `[${item.level}] ${item.message}`,
  awsConfig: { region: 'us-east-1', credentials: myCredentialProvider },
})

const logger = pino({ level: 'info' }, stream)

Configuration Options

| Option | Type | Required | Default | Description | |----------------------|------------------------------|----------|----------------|----------------------------------------------------------------------------------------------| | logGroupName | string | Yes | – | CloudWatch log group name (1–512 chars) | | logStreamName | string | No | <hostname>-<pid> | Log stream name (1–512 chars). Supports {hostname} {pid} {date} {time} tokens | | awsConfig | CloudWatchLogsClientConfig | No | {} | AWS SDK v3 client config (region, endpoint, credentials, …). Ignored if cloudWatchLogs is set | | cloudWatchLogs | CloudWatchLogsClient | No | – | Pre-built AWS SDK client (in-process usage only). Not destroyed on close | | createLogGroup | boolean | No | false | Auto-create the log group on first submission | | createLogStream | boolean | No | false | Auto-create the log stream on first submission | | retentionInDays | RetentionInDays | No | – | Set the log-group retention policy (e.g. 7, 30, 365) | | timeout | number | No | 10000 | Timeout (ms) for each AWS SDK call | | maxEventSize | number | No | 1048576 | Max event size in bytes (incl. 26-byte overhead); longer messages are truncated | | jsonMessage | boolean | No | false | Emit each event as a JSON object. Ignored if formatLog/formatLogItem is set | | formatLog | (item) => string | No | – | Custom message formatter (in-process only). Takes precedence over formatLogItem | | formatLogItem | (item) => {message,timestamp} | No | – | Custom message+timestamp formatter (in-process only) | | levelLabels | Record<number,string> | No | pino defaults | Override the numeric-level → label map (merged over 10..60) | | messageKey | string | No | 'msg' | Record key holding the message. Set to match a custom pino messageKey | | timestampKey | string | No | 'time' | Record key holding the timestamp. Set to match a custom pino timestamp key | | levelKey | string | No | 'level' | Record key holding the level. Set to match a custom pino levelKey | | onError | (error) => void | No | stderr warning | Delivery-failure reporter (in-process only). Default writes one line to stderr | | submissionInterval | number | No | 2000 | Minimum ms between batch submissions | | batchSize | number | No | 20 | Max log events per batch | | maxQueueSize | number | No | 10000 | Max queued events (oldest dropped when full) | | maxRetries | number | No | 10 | Consecutive head-batch failures before the batch is dropped (frees head-of-line) | | retryBackoffCap | number | No | 30000 | Upper bound (ms) on exponential backoff between retries; 0 disables backoff |

Backpressure & Delivery Semantics

CloudWatch delivery is decoupled from pino's log stream. Each record is accepted into a bounded in-memory queue and the stream keeps draining; delivery to CloudWatch then happens asynchronously in rate-limited batches.

This is deliberate. If delivery were coupled to the inbound stream, any persistent failure (throttling, timeouts, missing IAM, a CloudWatch outage, or an EMFILE/ulimit storm) would stall the pipeline head-of-line and buffer every later log unbounded until the process ran out of memory — the failure mode reported upstream in #36 and #37.

Practical implications:

  • Memory is strictly bounded by maxQueueSize, regardless of CloudWatch availability. When full, the oldest queued event is dropped.
  • A logging call returning does not mean the log reached CloudWatch — only that it was queued. Genuine delivery failures go to onError (or stderr).
  • During a persistent outage a failing batch is retried with exponential backoff and, after maxRetries consecutive failures, dropped — so an undeliverable head batch never blocks newer logs.

Graceful Shutdown / Flush

The transport implements pino's async close hook: on teardown it performs a best-effort flush of the queue (bounded by the flush timeout) before stopping. With a worker transport, await logger.flush() and let the process end so pino tears the worker down cleanly; the transport drains on close.

AWS Credentials

AWS SDK v3 resolves credentials from the standard chain (env vars, shared config files, IAM roles for EC2/ECS/Lambda). In a worker transport the chain runs inside the worker, so IAM roles and assumed-role-via-config work automatically. For a programmatic credential provider (e.g. a refreshing assumed-role provider — upstream #35), use the in-process form and pass it as awsConfig.credentials.

Security considerations

This transport accepts trusted, developer-supplied configuration and ships log content to AWS. A few notes for hardened/multi-tenant deployments:

  • awsConfig.endpoint must be a trusted HTTPS URL. It is passed straight to the AWS SDK client; a value sourced from untrusted input could redirect log batches to an attacker-controlled host (SSRF), and a plain http:// endpoint sends log content in clear text. Never populate it from untrusted data.
  • The default onError writes the raw provider error message to stderr so failures aren't silent. Provider messages can include operational metadata (endpoint, region, request id) — not your secret key, which the SDK never echoes. On a shared host, supply your own onError to control this output.
  • DEBUG=pino-cloudwatch:* logs option objects, which may include awsConfig.credentials. Enable debug logging only in trusted environments.
  • Log message and metadata are written verbatim (not sanitized for terminal rendering); downstream viewers that interpret ANSI/newlines are the consumer's responsibility.

Migration

Coming from the original pino-cloudwatch? See docs/migration-from-pino-cloudwatch.md. Every open issue and PR from the upstream project and how this rewrite addresses it is catalogued in docs/upstream-issue-audit.md.

Requirements

  • Node.js >= 20.9.0
  • pino ^8 || ^9

Development

pnpm install
pnpm test          # format + lint + unit (100% coverage required)
pnpm test:stress   # sustained memory soak (node --expose-gc; not in CI)
pnpm build

This project follows the mission-critical TypeScript standard in docs/CodingStandards.md.

License

MIT — see LICENSE.

Maintenance & credits

This package is the actively-maintained successor to the original pino-cloudwatch, which is no longer maintained. File bug reports and feature requests against this repository.

The original pino-cloudwatch by David Howell is gratefully acknowledged — its design informed this work. TypeScript v7-transport rewrite, AWS SDK v3 migration, and ongoing maintenance by Michael Lee Hobbs.