Step 1: Register

You need two things: a Whale account, and an API key so Whale knows the data is yours.

Create your account

  1. Go to app.whale.balajitech.xyz/register.
  2. Choose a plan. Free needs no card, so you can start there and move up later.
  3. Enter your details — your name, email and a password of at least 8 characters. Company is optional.
  4. Verify your email. We email you a 6-digit code that works for 15 minutes. Nothing arrived? Check your spam folder, or choose Send a new code.
  5. That’s it — you’re signed in to your dashboard.

Create an API key

  1. In the dashboard, open API keys — the last item in the left sidebar, under Account. (The sidebar shows icons until you point at it.)
  2. Type a name you’ll recognise later, like production. Leave App empty.
  3. Click Create key, then Copy. A new key starts working within 30 seconds.
Copy it now — it’s shown only once.Whale keeps only a hash of the key. If you lose it, create a new one and revoke the old one.
Secret or publishable?A normal key (whk_…) is a secret: use it on servers, and keep it out of your code and out of web pages. Tick Publishable to get a whk_pub_… key instead — it can only send browser and mobile events, so it’s safe inside a website. You only need one to monitor a website.

Step 2: Install

Pick what you want to monitor. You can set up several — everything lands in the same account.

Node.js app

NeedsNode.js 20.6 or newer.

  1. Install the package in your app’s folder:
    terminal
    npm install whalewatch
  2. Start your app with Whale loaded first. Use your own entry file instead of app.js, and the key you copied instead of your-key:
    terminal
    WHALE_API_KEY=your-key \
      node --import whalewatch/register app.js

    On Windows (PowerShell), set the key with $env:WHALE_API_KEY="your-key" first, then run the same node command without the key in front.

  3. Use your app for a moment — open a page or call an endpoint. Within a minute it shows up in the dashboard, in the app called default.

That’s all the code you write. Whale records every incoming request, outgoing call and database query — Express, Fastify, Koa, Postgres, MySQL, MongoDB, Redis, GraphQL, Kafka and about 40 more — plus console, pino, winston and bunyan logs, each linked to the request that wrote it.

Starting it with npm start, Docker or PM2? See Node.js in production.

Linux server

NeedsLinux with systemd (Ubuntu, Debian, Amazon Linux, RHEL and similar), x86-64 or ARM64, and sudo.

  1. Run this on the server, with your key:
    on the server
    curl -fsSL https://app.whale.balajitech.xyz/agent/install.sh |
      sudo bash -s -- --key your-key
  2. The server shows up in Infrastructure → Hosts within a minute.

This installs whale-agent: the OpenTelemetry Collector, pinned to a checked version and run as a systemd service. It reports CPU, memory, load, disks and network, the busiest processes, Docker containers, and on AWS the instance type, region and zone. Command lines never leave the server, because they can contain secrets.

To upgrade, run the same command again. To tail log files or remove the agent, see Server options.

Website

NeedsA publishable key (whk_pub_…). Never put a secret key in a web page.

  1. Install the package:
    terminal
    npm install whalewatch-browser
  2. Call it once when your site starts — for example in main.js:
    main.js
    import { initWhaleRum } from 'whalewatch-browser'
    
    initWhaleRum({
      key: 'whk_pub_…',      // your publishable key
      service: 'my-website', // the name you'll see in Whale
    })
  3. Open a few pages, then switch tabs or close one — measurements are sent when a page is hidden. They appear under Apps → Browser.

No build step? Paste this just before </body> instead:

index.html
<script type="module">
  import { initWhaleRum } from 'https://cdn.jsdelivr.net/npm/whalewatch-browser@0.1.0/+esm'
  initWhaleRum({ key: 'whk_pub_…', service: 'my-website' })
</script>

It records page views and the Core Web Vitals (LCP, INP, CLS, FCP and TTFB). It sets no cookies and doesn’t fingerprint visitors. In Next.js, call it from a client component, inside useEffect.

Python, Java, Go, .NET and others

Whale accepts standard OpenTelemetry, so you use the official OpenTelemetry SDK or agent for your language and point it at Whale with these environment variables:

environment
export OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.whale.balajitech.xyz
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS=x-api-key=your-key
export OTEL_SERVICE_NAME=checkout-api
Keep the protocol line.Some SDKs, including Python and .NET, send over gRPC by default, which the hosted service doesn’t accept yet. http/protobuf works everywhere.

Python — no code changes

terminal
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

# with the variables above set
OTEL_LOGS_EXPORTER=otlp opentelemetry-instrument python app.py

Java — no code changes

terminal
curl -LO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

# with the variables above set
java -javaagent:opentelemetry-javaagent.jar -jar app.jar

For other languages, follow the OpenTelemetry guide for your language and use the same variables. In Docker, Kubernetes or a .env file, set the same names without export. Already run an OpenTelemetry Collector? See OpenTelemetry Collector.

Step 3: Integrate

Make Whale part of how your software really runs: clear names, loaded on every start, and the rest of your stack connected.

Name your services, apps and environments

Whale sorts your data into three levels. The defaults work, but clear names make the dashboard much easier to read.

Service

One program — an API, a worker, a cron job.

Example checkout-apiDefault name in package.jsonSet with WHALE_SERVICE

App

The services that make up one product. Each app gets its own pages in the dashboard.

Example storefrontDefault defaultSet with WHALE_PROJECT

Environment

Where it runs, recorded on your logs and traces. For a fully separate staging view, give staging its own app.

Example productionDefault NODE_ENVSet with WHALE_ENV
Your account
├─ storefront      app
│  ├─ checkout-api service
│  ├─ web          service
│  └─ worker       service
└─ internal-tools  app
   └─ admin-api    service

Set them next to your key, wherever your app gets its environment:

environment
export WHALE_API_KEY=your-key
export WHALE_SERVICE=checkout-api
export WHALE_PROJECT=storefront
export WHALE_ENV=production
Apps create themselves.A new app appears the first time data arrives with its name. You can also create an empty one from the app picker, with + New app.

Why WHALE_PROJECT? Apps used to be called projects, and the setting kept its name. With an OpenTelemetry SDK, set the app and environment as resource attributes:

environment
export OTEL_RESOURCE_ATTRIBUTES=service.namespace=storefront,deployment.environment.name=production

Node.js in production

Whale has to load before your own code, so it can hook into Express, your database driver and the rest. The simplest way is to add --import whalewatch/register to whatever starts your app.

npm start

package.json
{
  "scripts": {
    "start": "node --import whalewatch/register app.js"
  }
}

Docker

Dockerfile
CMD ["node", "--import", "whalewatch/register", "app.js"]
terminal
docker run -e WHALE_API_KEY=your-key -e WHALE_SERVICE=checkout-api my-image

PM2

ecosystem.config.js
module.exports = {
  apps: [{
    name: 'checkout-api',
    script: 'app.js',
    node_args: '--import whalewatch/register',
    env: {
      WHALE_API_KEY: process.env.WHALE_API_KEY,
      WHALE_ENV: 'production',
    },
  }],
}

PM2 reads the environment when the app starts. After changing a variable, run pm2 restart checkout-api --update-env.

systemd

/etc/systemd/system/checkout-api.service
# in your app's unit file
[Service]
WorkingDirectory=/srv/checkout-api
Environment=WHALE_API_KEY=your-key
ExecStart=/usr/bin/node --import whalewatch/register app.js

Can’t change the start command?

Set NODE_OPTIONS=--import whalewatch/register in that app’s own environment instead — your hosting platform’s settings, or a Dockerfile ENV placed after npm ci. Don’t export it in your shell profile: every Node command you run would then try to load Whale.

Or load it on the first line (CommonJS, Node 22.12+)

If your app uses require, you can load Whale at the very top of your entry file instead. Everything required below that line is recorded.

app.js
require('dotenv').config()      // if you use dotenv, keep it first
require('whalewatch/register')  // before any other require

const express = require('express')

Apps that use import (ES modules) need --import.

Keep the key out of your code.Put WHALE_API_KEY in your host’s secret settings or in a .env file that isn’t committed. Use one key per environment, so you can revoke one without touching the others.

Add your own spans (optional)

Wrap work that isn’t a request or a query — charging a card, building a report — to see it in traces too:

your code
import { trace } from 'whalewatch'

const tracer = trace.getTracer('billing')
await tracer.startActiveSpan('charge-card', async (span) => {
  try {
    await chargeCard()
  } finally {
    span.end()
  }
})

Server options

Add options to the install command. Running it again updates the agent — include every option you want each time, because each run starts from the defaults. The new config is checked before it replaces the running one.

on the server — also tail log files
curl -fsSL https://app.whale.balajitech.xyz/agent/install.sh |
  sudo bash -s -- --key your-key \
    --logs 'nginx=/var/log/nginx/*.log' \
    --logs 'api=/srv/api/logs/*.log'
OptionWhat it does
--logs NAME=GLOBTail log files; each NAME shows up as its own service in Logs. Repeatable.
--project APPThe app to file this server under (default: default).
--host NAMEReport this name instead of the machine’s hostname.
--no-dockerNever report containers.

To remove the agent and everything it installed:

on the server
curl -fsSL https://app.whale.balajitech.xyz/agent/install.sh | sudo bash -s -- --uninstall

All options are in the Reference. The agent’s own logs: sudo journalctl -u whale-agent. Also running a Node.js app with Whale on the same server? Set WHALE_HOST_METRICS=false in that app so the machine isn’t reported twice.

Connect your AWS account

Nothing to install in AWS. Whale reads your account from outside, with a key that can look but can’t change anything.

  1. Create an IAM user for Whale in the AWS console (it doesn’t need console access). Attach the read-only policy Whale gives you: open AWS in Whale’s sidebar and expand Least-privilege policy for the IAM user.
  2. Create an access key for that user: IAM → Users → the user → Security credentials → Create access key.
  3. Back on Whale’s AWS page, enter a name, the Access key ID, the Secret access key and your Regions (for example ap-south-1, us-east-1), then click Connect. Already connected one? Add more with + Connect AWS.
  4. Your account map appears within a minute or so, and CloudWatch metrics within five. After that the map is redrawn every 10 minutes and metrics arrive every 5.

Whale checks the key with AWS before saving it, stores it encrypted, and never shows it again. Root keys are refused. Route 53 and CloudFront come along with the first region.

Or let AWS push to Whale

Deploy the whale-aws CloudFormation stack once per account and region instead. It streams CloudWatch metrics through Firehose and maps load balancers, EC2, RDS, ElastiCache, Lambda and SQS — fewer services than a connected key, and no network layout.

terminal
curl -fsSLO https://app.whale.balajitech.xyz/agent/aws/whale-aws.yaml
aws cloudformation deploy --stack-name whale-aws \
  --template-file whale-aws.yaml --capabilities CAPABILITY_IAM \
  --region ap-south-1 --parameter-overrides WhaleIngestKey=your-key

Already using OpenTelemetry?

Add an otlphttp/whale exporter to your OpenTelemetry Collector, then add it to the exporters of each pipeline, next to the ones already there:

collector config
exporters:
  otlphttp/whale:
    endpoint: https://ingest.whale.balajitech.xyz
    headers:
      x-api-key: ${env:WHALE_API_KEY}

service:
  pipelines:
    traces:
      exporters: [otlphttp/whale]   # plus your existing exporters
    metrics:
      exporters: [otlphttp/whale]
    logs:
      exporters: [otlphttp/whale]

Moving from another vendor

Send to both for a week, check the data matches, then remove the old exporter:

collector config
service:
  pipelines:
    traces:
      exporters: [otlphttp/incumbent, otlphttp/whale]

Step 4: Use

Check that data is arriving, learn where things are, and try what people do most.

Check that data is arriving

Open Home in the dashboard. Every app that has sent data is listed with its traffic, errors and latency — click one to open its pages.

Want to test your key on its own? Send one log line with curl:

terminal
curl https://ingest.whale.balajitech.xyz/v1/logs \
  -H "x-api-key: your-key" \
  -H "content-type: application/json" \
  -d '{"resourceLogs": [{
    "resource": {"attributes": [
      {"key": "service.name", "value": {"stringValue": "hello-whale"}}
    ]},
    "scopeLogs": [{"logRecords": [
      {"severityNumber": 9, "severityText": "INFO", "body": {"stringValue": "Hello, Whale"}}
    ]}]
  }]}'

A reply of {} means it worked — search Logs for service:hello-whale. missing or invalid x-api-key means the key is wrong.

Find your way around

The left sidebar shows icons — point at it to see the names.

Home
Every app at a glance: traffic, errors, latency and what needs attention.
Apps
Opens a second menu for one app — pick it at the top, or choose All apps. Summary, Logs, Metrics, Traces, Services, Databases, External services, Hosts, Browser, Mobile and Issues; then Alerts, Service levels, Dashboards, Uptime and AI.
Infrastructure
All your servers, plus services like Redis and queues, and routes to watch — across every app.
AWS
A live map of your AWS account, with CloudWatch metrics and problems shown where they are.
Account
Organisation, Usage, Billing, Obfuscation (masks secrets before anything is stored), Team, Roles, Single sign-on and API keys.
Search
Press ⌘K (Ctrl+K on Windows and Linux) to jump to any page, app, service, host or log search.

Everyday tasks

Search your logs

Apps → Logs

Type what you’re looking for. A few searches to start with:

  • service:checkout-api severity>=ERROR | last 1h
    Errors from one service in the last hour
  • body:"payment timeout"
    Lines that contain a phrase
  • severity>=WARN -body:healthcheck
    Warnings and worse, without health-check noise
  • severity>=ERROR | count by service
    Which service logs the most errors

See what’s broken

Apps → Issues

Errors grouped by their stack trace, with how often they happen, when they started, and the requests behind them.

Follow a slow request

Apps → Traces

Open a trace to see every step it took and how long each one lasted, with the logs written along the way.

Get alerted

Apps → Alerts

Create a rule — say, more than 10 logs matching severity>=ERROR in 5 minutes — and send it to Slack, a webhook or email.

Watch a website or port

Apps → Uptime

Add an HTTP or TCP check. Whale tests it every 30 seconds by default and can show the results on a public status page.

Bring in your team

Account → Team

Invite people by email and pick their role. Roles sets what each role can see and change.

Keep an eye on usage

Account → Usage

See how many events you’ve stored and how much disk they take, by signal.

What’s included depends on your plan — see Pricing.

Troubleshooting

Nothing shows up in the dashboard

  1. Restart your app with WHALE_DEBUG=1 to see what the SDK is doing. If it prints WHALE_API_KEY is not set, the key isn’t reaching your app: Docker and systemd don’t see variables from your terminal, and PM2 only picks up a change with --update-env.
  2. Check that node -v is 20.6 or newer (22.12 or newer for the require line).
  3. Send the curl test to check the key on its own.
  4. Make sure the machine can reach ingest.whale.balajitech.xyz on port 443 (HTTPS).

“missing or invalid x-api-key” (HTTP 401)

The key is mistyped, was revoked, or was created less than 30 seconds ago — or it’s a publishable whk_pub_… key used on a server. Those only work for browser and mobile events; create a normal key for servers.

Logs arrive, but no requests or traces

Whale loaded too late. Use --import or NODE_OPTIONS, or put require('whalewatch/register') on the very first line. In an ES-module app ("type": "module" or .mjs), only --import works.

A script’s last few events are missing

process.exit() skips the final send. Let the process end on its own, or call await shutdown() (imported from whalewatch) first.

A Python or .NET app sends nothing

Set OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf. Those SDKs default to gRPC, which the hosted service doesn’t accept yet.

A server isn’t reporting

Run sudo systemctl status whale-agent and sudo journalctl -u whale-agent -n 50. The installer checks your key before installing anything. If it says the key was rejected, check it for typos — and if you created it in the last 30 seconds, wait a moment and run the command again.

No browser data

Measurements are sent when a page is hidden, so switch tabs or close one. initWhaleRum quietly does nothing if key or service is missing. If your site sets a Content Security Policy, allow https://ingest.whale.balajitech.xyz in connect-src. Ad blockers can block it too.

Still stuck? Email us with what you tried and what you saw.

Reference

Node.js SDK settings

Only WHALE_API_KEY is required. Everything else has a sensible default.

VariableDefaultWhat it does
WHALE_API_KEY—Required. Your API key.
WHALE_SERVICEyour package.json nameService name in the dashboard. OTEL_SERVICE_NAME works too.
WHALE_PROJECTdefaultThe app this service belongs to.
WHALE_ENVNODE_ENV, else developmentEnvironment, e.g. production.
WHALE_VERSIONnpm_package_versionRelease label, so you can tell deploys apart.
WHALE_HOSTthe machine’s hostnameInstance name.
WHALE_CAPTURE_CONSOLEtrueSend console.* output as logs.
WHALE_CAPTURE_ERRORStrueRecord uncaught exceptions and unhandled promise rejections.
WHALE_HOST_METRICStrueSend the machine’s CPU, memory and network. Turn off where whale-agent runs.
WHALE_METRICS_INTERVAL_MS60000How often metrics are sent, in milliseconds.
WHALE_DEBUGfalsePrint SDK diagnostics to stderr.
WHALE_DISABLEDfalseTurn the SDK off without uninstalling it.
WHALE_URLWhale’s hosted ingestOnly for a self-hosted Whale.

Server agent options

OptionWhat it does
--key KEYYour API key. Required to install.
--logs NAME=GLOBTail log files as a service called NAME (lowercase letters, digits, - and _). Separate several globs with commas. Repeatable.
--project APPThe app to file this server under (default: default).
--host NAMEReport this name instead of the machine’s hostname.
--interval SECSHow often to send host metrics, 10–3600 seconds (default 30).
--no-processesDon’t report per-process CPU and memory.
--dockerReport containers even if Docker isn’t running yet.
--no-dockerNever report containers.
--print-configPrint the Collector config and exit, to run it in Docker or Kubernetes yourself.
--root-path PATHWhere the host’s / is mounted, when the agent runs in a container.
--url URLOnly for a self-hosted Whale.
--uninstallStop the agent and remove everything it installed.

Browser SDK options

OptionDefaultWhat it does
key—Required. A publishable key (whk_pub_…).
service—Required. Your site’s name in Whale.
projectdefaultThe app it belongs to.
sampleRate1Share of page loads to record, from 0 to 1 (0.1 = 10%).
urlWhale’s hosted ingestOnly for a self-hosted Whale.

Record your own events next to the vitals:

main.js
import { recordRumEvent } from 'whalewatch-browser'

recordRumEvent('checkout_step', 2, { plan: 'pro' })

Ingest endpoints

Hosted ingest: https://ingest.whale.balajitech.xyz. Send your key in the x-api-key header. The browser SDK and the AWS stack send it for you.

PathAcceptsKey
POST /v1/logs · /v1/metrics · /v1/tracesOTLP over HTTP, protobuf or JSON — any OpenTelemetry SDK or CollectorSecret
POST /v1/rumBrowser events from whalewatch-browserPublishable
POST /v1/aws/firehoseThe CloudWatch metric stream from the whale-aws stack, which sets it up for youSecret
OTLP over gRPCSelf-hosted installs only (port 4317) — use OTLP over HTTP for the hosted serviceSecret

Self-hosting and help

Need Whale inside your own network? On Enterprise plans it runs on your infrastructure against your own ClickHouse — same platform, same dashboard. Most teams start hosted and move only if compliance requires it.