Separate staging and production logs
Your staging servers send logs to the same place as production. A load test in staging trips the production error alert at 2am. A search for "payment failed" brings back test cards next to real customers. You can add environment:production to every query and every alert rule, but someone always forgets.
ZipLogger workspaces now have environments: Production, Staging, Development or any other you add. Each one has its own logs, traces, metrics, events and replays, its own alert rules and incidents, and its own API keys. You sign in once and switch between them.
What belongs to an environment, and what does not
| Each environment has its own | Shared by the whole workspace |
|---|---|
| Logs, traces, metrics, events, session replays | Members, roles, single sign-on, branding |
| Ingest and read API keys | Plan, billing and usage |
| Alert rules, incidents, job monitors | On-call schedules, escalation policies |
| Browser monitors and their runs | Slack, Teams and other outbound destinations |
| Inbound alert integrations (webhooks) | Dashboard and saved report definitions |
| Releases, regressions, error patterns | Retention settings, maintenance windows |
Dashboards are defined once. The same dashboard shows Staging's data while you are in Staging and Production's data while you are in Production, so you do not keep two copies in sync.
Add a Staging environment
Go to Settings → Environments. Only workspace admins can add or change environments. Everyone else sees the list.
Under Add an environment, pick a type: Production, Staging, Development, QA, Sandbox or Custom. The name is filled in for you. The Key is optional. If you leave it empty, it is made from the name, so Staging gets the key staging. The key is what appears in addresses and is stored on every record, so it cannot be changed later and is never reused, even after the environment is archived. The name can be changed at any time.
Leave Create an ingest API key for it ticked and click Create environment. The new key is shown once, with a curl command you can copy. Copy the key now.
How many environments you can have depends on your plan:
| Plan | Environments |
|---|---|
| Free, Pro, Team | 1 |
| Business | 3 |
| Enterprise (plans with single sign-on) | No limit |
When all of them are in use, the page says so and links to an upgrade. An archived environment does not count.
The API key decides where data goes
Every API key belongs to one environment, for life. Whatever arrives with a Staging key is stored in Staging. Nothing in the request can change that: not a field in the body, not a header, not a query string parameter, and not the OpenTelemetry deployment.environment attribute.
curl -X POST https://app.ziplogger.ai/ingest/v1/logs \
-H "X-Api-Key: YOUR_STAGING_KEY" \
-H "Content-Type: application/json" \
-d '{"message":"hello from staging","severity":"info","source":"orders-api"}'
This line lands in Staging. If you add "environment":"production" to the body, it still lands in Staging. The server's tests send exactly that, with a Staging key, a production header, ?env=production and the same value inside the attributes, and check that the record is stored in Staging.
The same rule covers every way in: the native endpoints, OTLP logs, traces and metrics, Sentry DSNs (the DSN contains the key), the browser SDK and session replay.
You can make more keys later under Settings → API keys. The form has an Environment menu next to the key name. It is set to the environment you are viewing, and it cannot be changed once the key exists. The key list shows This environment by default, and All environments shows every key in the workspace.
One key per deployment
There is nothing to change in the SDKs. Give each deployment the key of its environment, the same way you already give it a database password.
# staging deployment
ZIPLOGGER_API_KEY=zk_... # key created in Staging
ZIPLOGGER_ENVIRONMENT=staging
# production deployment
ZIPLOGGER_API_KEY=zk_... # key created in Production
ZIPLOGGER_ENVIRONMENT=production
const { ZipLoggerClient } = require('ziplogger')
const log = new ZipLoggerClient({
endpoint: 'https://app.ziplogger.ai',
apiKey: process.env.ZIPLOGGER_API_KEY,
source: 'orders-api',
})
For OpenTelemetry, it is the key in OTEL_EXPORTER_OTLP_HEADERS=X-Api-Key=zk_... that differs per deployment. For a browser app, build the staging bundle with the staging key. A browser key should be an ingest-only key either way.
ZIPLOGGER_ENVIRONMENT is not what puts the data in Staging. The key does that. The next section explains what the variable is for.
The environment field is a different thing
Before environments existed, the usual way to tell staging from production was a field. The ZipLogger SDKs still add one to every log line. In Node it comes from the environment option, then ZIPLOGGER_ENVIRONMENT, then NODE_ENV, then production. We ran the client above against a local HTTP catcher with a staging key and NODE_ENV=production. This is the part of the request that matters (host name changed):
{
"message": "Order created",
"source": "orders-api",
"fields": {
"environment": "production",
"machineName": "web-01",
"orderId": 83112
}
}
The key was a staging key, so ZipLogger stores this line in Staging. The field inside it still says production, because many staging servers run with NODE_ENV=production. With ZIPLOGGER_ENVIRONMENT=staging set, the field says staging.
So there are now two things called environment:
- The environment you pick in the app. It comes from the API key and decides where data is stored and which view shows it.
- The
environmentfield. It is ordinary data that your SDK or your code sends. ZipLogger keeps it, and you can search and filter on it, but it never moves a record from one environment to another.
These places in the app still read the field, not the environment:
- The Environment filter on dashboards. On log widgets it matches
fields.environment, on event widgets the event'senvironment. - The Environment column and filter on the Events page, the environment filter on the Investigation page, and grouping by Environment on the Usage page.
- The Environment box on job monitors, maintenance windows and alert destinations, and Default environment on inbound integrations. These are free-text labels.
If you send the field, keep it in step with the key, or you will get a Staging view full of lines that say production. If you do not care about it, you can ignore it.
Moving between environments in the app
The environment selector sits at the top of the sidebar, above the menu, and shows where you are, for example ● Staging ▾. Admins also get Manage environments at the bottom of it.
In any environment whose type is not Production, a band across the top of every page says "STAGING · You are viewing the Staging environment", and the browser tab title starts with [Staging]. It cannot be dismissed. A Production environment shows neither.
The environment is part of the address: /env/staging/traces, /env/staging/alerts/incidents/<id>. A link you paste into a chat opens in the environment it was copied from. If you switch environments while looking at one incident, trace or replay, you land on the list instead, because that record does not exist in the other environment. Without /env/ in the address, the app opens the environment you used last in that workspace, or else the default.
Any environment can be made the default with Make default. The default is where the app and the API go when nothing names an environment.
Reading from scripts and the API
The app sends the header X-ZipLogger-Environment with every request. Your scripts can do the same. The order is: the header, then the env query string parameter, then the workspace default.
curl -sG https://app.ziplogger.ai/api/v1/logs/search \
-H "Authorization: Bearer $TOKEN" \
-H "X-ZipLogger-Environment: staging" \
--data-urlencode "q=payment failed" \
--data-urlencode "size=50"
$TOKEN is a sign-in token from POST /api/v1/auth/login, the same as in the query logs over HTTP post. The response repeats the environment in its own X-ZipLogger-Environment header. A key that is not an environment of your workspace answers 404 with the code environment_not_found. An archived one answers 409 with environment_archived.
Scripts that already exist and send no header keep working. They read the default environment, which is Production unless you changed it.
Read keys work differently. A read key used by Grafana, the Loki endpoint or the MCP server can only read the environment it was created in, whatever the query asks for. To read Staging from Grafana, make a read key in Staging. An AI assistant connected through OAuth starts in the default environment, and the MCP tools take an environment argument. The list_environments tool lists the environments and their keys.
Alerts and incidents stay in their environment
An alert rule belongs to the environment you were in when you created it, and it only counts that environment's data. A rule on source:checkout-api in Production does not fire on staging's checkout errors. The same holds for job monitors, browser monitors and inbound integrations. The incident they open belongs to that environment too, and the Alerts page lists only the incidents of the environment you are viewing.
When an incident from any environment other than the primary one pages someone by email, SMS, call or webhook, the alert text starts with the environment name in brackets, such as [Staging]. Links in every notification, Slack and Teams included, open the incident in its own environment.
Two things to know here:
- Slack and Teams messages do not name the environment yet. The title and fields are the same as before. The link is correct, but the message does not say "Staging".
- On-call and destinations are shared. Escalation policies, on-call schedules and destinations belong to the workspace. A destination's Environment box matches the free-text label, not the environment, and incidents from alert rules carry no label. So you cannot yet send only Staging's alert rule incidents to a
#stagingchannel. Keep pages out of Staging by not putting escalation policies on Staging rules.
Your existing data and keys
Nothing was moved or rewritten. Every workspace got one environment called Production, marked primary. It holds everything sent before environments existed, and every key you already had sends there. Existing alert rules, monitors and incidents are in it too. If you never add a second environment, nothing changes for you.
The primary environment cannot be archived. The default cannot be archived either: make another one the default first.
Archiving an environment
Archive on the Settings page asks first, then:
- refuses anything sent with that environment's keys, with
401 - removes it from the selector
- pauses its alert rules and monitors
- keeps its data until the workspace's retention removes it
Restore brings it back, keys included. There is no delete button.
If you move to a plan with fewer environments, nothing is archived for you. The extra environments are marked over plan limit. They keep working, but you cannot make new keys in them, or add another environment, until you archive one or upgrade.
What environments do not do
- No per-environment permissions. Every member can see and switch to every environment, and roles apply to the whole workspace. You cannot make Production admin-only.
- One bill. Usage from every environment adds up against the workspace's allowances. Staging does not get its own free allowance, and the Usage page counts all environments together.
- Plan limits are per workspace. Keys, alert rules and browser monitors in all environments count toward the same limits.
- Nothing is copied. A new environment starts empty. Alert rules, monitors and integrations are not copied from Production, so create the ones Staging needs.
- No moving data. A record stays in the environment its key put it in. A key cannot be moved to another environment, and an environment's key cannot be renamed.
- Field names are shared. A custom field or event property that only Staging sends can show up in Production's suggestions. The values and counts are per environment.
- One retention schedule and one set of maintenance windows for the whole workspace.
To set one up, open Settings → Environments in your workspace, or create a free workspace to try the rest first. More than one environment needs the Business plan or Enterprise, see pricing. The API keys section of the docs covers creating, rotating and revoking keys.