Docs
Get started with Whale
Four steps from sign-up to your first trace. Most setups take about five minutes.
- Step 1: RegisterCreate your account and an API key.
- Step 2: InstallAdd Whale to a Node.js app, a Linux server, a website, or any OpenTelemetry app.
- Step 3: IntegrateRun it in production: names, start commands, Docker, PM2, AWS.
- Step 4: UseCheck your data, find errors, search logs and set alerts.
Stuck? See Troubleshooting. Looking for a setting? See the Reference.
Step 1: Register
You need two things: a Whale account, and an API key so Whale knows the data is yours.
Create your account
- Go to app.whale.balajitech.xyz/register.
- Choose a plan. Free needs no card, so you can start there and move up later.
- Enter your details — your name, email and a password of at least 8 characters. Company is optional.
- 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.
- That’s it — you’re signed in to your dashboard.
Create an API key
- In the dashboard, open API keys — the last item in the left sidebar, under Account. (The sidebar shows icons until you point at it.)
- Type a name you’ll recognise later, like
production. Leave App empty. - Click Create key, then Copy. A new key starts working within 30 seconds.
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.
- Install the package in your app’s folder:terminal
npm install whalewatch
- Start your app with Whale loaded first. Use your own entry file instead of
app.js, and the key you copied instead ofyour-key:terminalWHALE_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 samenodecommand without the key in front. - 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.
- 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
- 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.
- Install the package:terminal
npm install whalewatch-browser
- Call it once when your site starts — for example in
main.js:main.jsimport { initWhaleRum } from 'whalewatch-browser' initWhaleRum({ key: 'whk_pub_…', // your publishable key service: 'my-website', // the name you'll see in Whale }) - 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:
<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:
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
http/protobuf works everywhere.Python — no code changes
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
# with the variables above set
OTEL_LOGS_EXPORTER=otlp opentelemetry-instrument python app.pyJava — no code changes
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.jarFor 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.
App
The services that make up one product. Each app gets its own pages in the dashboard.
Environment
Where it runs, recorded on your logs and traces. For a fully separate staging view, give staging its own app.
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:
export WHALE_API_KEY=your-key export WHALE_SERVICE=checkout-api export WHALE_PROJECT=storefront export WHALE_ENV=production
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:
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
{
"scripts": {
"start": "node --import whalewatch/register app.js"
}
}Docker
CMD ["node", "--import", "whalewatch/register", "app.js"]
docker run -e WHALE_API_KEY=your-key -e WHALE_SERVICE=checkout-api my-image
PM2
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
# 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.jsCan’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.
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.
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:
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.
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'| Option | What it does |
|---|---|
--logs NAME=GLOB | Tail log files; each NAME shows up as its own service in Logs. Repeatable. |
--project APP | The app to file this server under (default: default). |
--host NAME | Report this name instead of the machine’s hostname. |
--no-docker | Never report containers. |
To remove the agent and everything it installed:
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.
- 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.
- Create an access key for that user: IAM → Users → the user → Security credentials → Create access key.
- 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. - 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.
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:
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:
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:
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 hourbody:"payment timeout"
Lines that contain a phraseseverity>=WARN -body:healthcheck
Warnings and worse, without health-check noiseseverity>=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
- Restart your app with
WHALE_DEBUG=1to see what the SDK is doing. If it printsWHALE_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. - Check that
node -vis 20.6 or newer (22.12 or newer for therequireline). - Send the curl test to check the key on its own.
- Make sure the machine can reach
ingest.whale.balajitech.xyzon 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.
| Variable | Default | What it does |
|---|---|---|
WHALE_API_KEY | — | Required. Your API key. |
WHALE_SERVICE | your package.json name | Service name in the dashboard. OTEL_SERVICE_NAME works too. |
WHALE_PROJECT | default | The app this service belongs to. |
WHALE_ENV | NODE_ENV, else development | Environment, e.g. production. |
WHALE_VERSION | npm_package_version | Release label, so you can tell deploys apart. |
WHALE_HOST | the machine’s hostname | Instance name. |
WHALE_CAPTURE_CONSOLE | true | Send console.* output as logs. |
WHALE_CAPTURE_ERRORS | true | Record uncaught exceptions and unhandled promise rejections. |
WHALE_HOST_METRICS | true | Send the machine’s CPU, memory and network. Turn off where whale-agent runs. |
WHALE_METRICS_INTERVAL_MS | 60000 | How often metrics are sent, in milliseconds. |
WHALE_DEBUG | false | Print SDK diagnostics to stderr. |
WHALE_DISABLED | false | Turn the SDK off without uninstalling it. |
WHALE_URL | Whale’s hosted ingest | Only for a self-hosted Whale. |
Server agent options
| Option | What it does |
|---|---|
--key KEY | Your API key. Required to install. |
--logs NAME=GLOB | Tail log files as a service called NAME (lowercase letters, digits, - and _). Separate several globs with commas. Repeatable. |
--project APP | The app to file this server under (default: default). |
--host NAME | Report this name instead of the machine’s hostname. |
--interval SECS | How often to send host metrics, 10–3600 seconds (default 30). |
--no-processes | Don’t report per-process CPU and memory. |
--docker | Report containers even if Docker isn’t running yet. |
--no-docker | Never report containers. |
--print-config | Print the Collector config and exit, to run it in Docker or Kubernetes yourself. |
--root-path PATH | Where the host’s / is mounted, when the agent runs in a container. |
--url URL | Only for a self-hosted Whale. |
--uninstall | Stop the agent and remove everything it installed. |
Browser SDK options
| Option | Default | What it does |
|---|---|---|
key | — | Required. A publishable key (whk_pub_…). |
service | — | Required. Your site’s name in Whale. |
project | default | The app it belongs to. |
sampleRate | 1 | Share of page loads to record, from 0 to 1 (0.1 = 10%). |
url | Whale’s hosted ingest | Only for a self-hosted Whale. |
Record your own events next to the vitals:
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.
| Path | Accepts | Key |
|---|---|---|
| POST /v1/logs · /v1/metrics · /v1/traces | OTLP over HTTP, protobuf or JSON — any OpenTelemetry SDK or Collector | Secret |
| POST /v1/rum | Browser events from whalewatch-browser | Publishable |
| POST /v1/aws/firehose | The CloudWatch metric stream from the whale-aws stack, which sets it up for you | Secret |
| OTLP over gRPC | Self-hosted installs only (port 4317) — use OTLP over HTTP for the hosted service | Secret |
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.