whalewatch
v0.5.0
Published
One-line auto-instrumentation for Node.js apps. Ships logs, metrics and traces to Whale.
Maintainers
Readme
whalewatch
One-line auto-instrumentation for Node.js. Ships logs, metrics and traces to Whale over OTLP.
No code changes. HTTP routes, database queries, outbound calls, errors and process health are captured automatically.
Install
npm install whalewatchUse
Add the preload flag and one environment variable:
WHALE_API_KEY=your-key node --import whalewatch/register app.jsSelf-hosting? Point it at your own instance:
WHALE_URL=http://localhost:4318 WHALE_API_KEY=your-key \
node --import whalewatch/register app.jsOr, without touching the start command:
export NODE_OPTIONS='--import whalewatch/register'
export WHALE_API_KEY=your-key
npm startThat's it. Open Whale and your service is there.
Configuration
Everything is optional except WHALE_API_KEY.
| Variable | Default | Purpose |
|---|---|---|
| WHALE_API_KEY | — | Required. Sent as the x-api-key header. |
| WHALE_URL | Whale's hosted endpoint | Self-hosting? Set this, or your data leaves your network. |
| WHALE_SERVICE | your package.json name | Service name shown in the UI. |
| WHALE_PROJECT | default | Project the signals are filed under. |
| WHALE_ENV | NODE_ENV | Deployment environment. |
| WHALE_VERSION | npm_package_version | Build/release label. |
| WHALE_HOST | os.hostname() | Instance label. |
| WHALE_CAPTURE_CONSOLE | true | Mirror console.* into Whale as logs. |
| WHALE_CAPTURE_ERRORS | true | Capture uncaught exceptions and unhandled rejections. |
| WHALE_METRICS_INTERVAL_MS | 60000 | Metric export interval. |
| WHALE_DISABLED | false | Turn the SDK into a no-op. |
| WHALE_DEBUG | false | Print SDK diagnostics to stderr. |
What you get automatically
- Traces — HTTP server and client, Express/Fastify/Koa/Hapi routes, Postgres, MySQL, MongoDB, Redis, GraphQL, gRPC, Kafka and ~40 more.
- Logs —
console.*plus pino, winston and bunyan, each correlated to the trace and span that was active when they were written. - Metrics — event-loop lag, garbage collection, heap and process health.
- Errors — uncaught exceptions and unhandled rejections, tagged with the
OTel
exception.*attributes so Whale's Issues screen groups them.
Programmatic use
Prefer the --import flag. If you need to configure in code, call start()
before anything else is imported — instrumentation cannot patch a module that
has already been loaded:
// instrument.js
import { start } from 'whalewatch'
start({
apiKey: process.env.WHALE_API_KEY,
service: 'agrizy-api',
project: 'agrizy',
})node --import ./instrument.js app.jsManual spans and metrics, without a second copy of the OTel API:
import { trace } from 'whalewatch'
const tracer = trace.getTracer('billing')
await tracer.startActiveSpan('charge-card', async (span) => {
try {
await charge()
} finally {
span.end()
}
})CommonJS apps, without changing the start command
If your app uses require, load the SDK on its first line instead — the same
setup the New Relic agent uses:
require('dotenv').config() // keep dotenv above it, if you use one
require('whalewatch/register') // must come before your other requires
const express = require('express')node server.js then instruments everything required below that line. Needs
Node 22.12+. ESM apps still need node --import whalewatch/register, because
their imports resolve before any statement runs — there is nothing left to
patch by the time the first line executes.
Notes
- Node ≥20.6 —
--importwas added there. Works for CommonJS apps too; the flag governs how the SDK is loaded, not your application. process.exit()skips the flush. Node runs no exit hooks on an explicitprocess.exit(), so any telemetry still in the batch buffer is lost. Let the event loop drain, orawait shutdown()first.- Never crashes your app. A failure to start is written to stderr and the application continues uninstrumented. That is deliberate: an observability SDK that can take down the host process is worse than no observability.
