Documentation
Everything you need to go from zero to searchable logs. Ten minutes, tops.
Getting started
From zero to searchable logs in four steps:
- Create your free account and sign in (details below).
- Create an ingestion API key under Settings → API keys.
- Add a ZipLogger package to your app, or point your OTel exporter at it.
- Open Search and watch your logs arrive live.
Your workspace
ZipLogger is a hosted service, so there is nothing to install, run, patch, or scale. Sign up,
and your workspace is ready in seconds at app.ziplogger.ai. Your applications keep
running wherever they already run (any cloud, container platform, or your own network) and simply
send their telemetry to ZipLogger over HTTPS.
Once you are in, invite your team under Team, create an ingestion key under Settings → API keys, and pick a plan under Billing (the Free plan is active by default, no card needed to start).
.NET integration
Two NuGet packages cover the standard .NET logging abstractions. Both share the same transport: a bounded in-memory queue, NDJSON batching, retry with exponential backoff (429-aware), and drop-on-backpressure: a logging call never blocks or throws in your application.
Both packages also enrich every event automatically with the release (assembly informational
version), commit SHA (SourceLink suffix or GIT_COMMIT), environment, and
machine name. The commit SHA is what powers git regression detection.
Serilog
dotnet add package ZipLogger.Serilog
Log.Logger = new LoggerConfiguration()
.WriteTo.ZipLogger("https://app.ziplogger.ai", "zk_...")
.CreateLogger();
Log.Information("Order {OrderId} created for {Customer}", 83112, "acme");
// → structured fields OrderId + Customer, searchable instantly
Destructured objects ({@Cart}), the message template, and exceptions are preserved as
structured fields; exceptions map to the stack-trace field that feeds regression detection.
Microsoft ILogger
dotnet add package ZipLogger.Extensions.Logging
// Program.cs
builder.Logging.AddZipLogger(options =>
{
options.Endpoint = "https://app.ziplogger.ai";
options.ApiKey = "zk_...";
});
Or configure from appsettings.json under Logging:ZipLogger, including
standard per-category level filtering. Scopes are captured as fields, and
logger.LogError(ex, ...) ships the full exception.
Python
pip install ziplogger
import logging
from ziplogger import ZipLoggerHandler
logging.getLogger().addHandler(ZipLoggerHandler(
endpoint="https://app.ziplogger.ai",
api_key="zk_...",
))
log = logging.getLogger("app.orders")
log.info("Order %s created", 83112, extra={"orderId": 83112})
log.exception("Payment failed") # traceback → stackTrace
Standard library only, no dependencies. extra= values become searchable fields,
the logger name becomes category, and tracebacks feed git regression detection.
Same delivery semantics as the .NET client: bounded queue, batching, 429-aware retries, and a
handler that never blocks or raises.
Node.js
npm install ziplogger
Pino
const pino = require('pino')
const logger = pino(pino.transport({
target: 'ziplogger/pino',
options: { endpoint: 'https://app.ziplogger.ai', apiKey: 'zk_...' },
}))
logger.info({ orderId: 83112 }, 'Order created')
Winston
const { ZipLoggerTransport } = require('ziplogger/winston')
const logger = winston.createLogger({
transports: [new ZipLoggerTransport({ endpoint: 'https://...', apiKey: 'zk_...' })],
})
A zero-dependency core client (ZipLoggerClient) is also exported for custom
setups. Error objects map to stackTrace; timers are unrefed so the
SDK never keeps your process alive.
Go
go get github.com/ziploggerhq/ZipLogger_Client/sdk_go
client, err := ziplogger.New(ziplogger.Options{
Endpoint: "https://app.ziplogger.ai",
APIKey: "zk_...",
})
defer client.Close(5 * time.Second)
logger := slog.New(ziplogger.NewSlogHandler(client, slog.LevelInfo))
logger.Info("order created", "orderId", 83112)
logger.Error("payment failed", "err", err) // → stackTrace
Standard library only. slog attributes become searchable fields (groups are
dot-prefixed); an err attribute maps to the exception fields that feed regression
detection. The direct client.Log(ziplogger.Entry{...}) API is there for
everything else.
Java
<dependency>
<groupId>dev.ziplogger</groupId>
<artifactId>ziplogger</artifactId>
<version>0.1.0</version>
</dependency>
var client = new ZipLoggerClient(new ZipLoggerClient.Options(
"https://app.ziplogger.ai", "zk_..."));
Logger.getLogger("").addHandler(new ZipLoggerJulHandler(client, true));
log.info("order created");
log.log(Level.SEVERE, "payment failed", exception); // → stackTrace
JDK-only (Java 17+), zero dependencies. A java.util.logging handler ships today;
log through the client directly from SLF4J/Logback setups; native appenders are on the
roadmap.
Browser / React
npm install @ziplogger/browser
import { ZipLoggerBrowser } from '@ziplogger/browser'
import { createErrorBoundary } from '@ziplogger/browser/react'
const zl = new ZipLoggerBrowser({ endpoint: 'https://logs...', apiKey: 'zk_...' })
zl.captureGlobalErrors() // window.onerror + unhandledrejection
const Boundary = createErrorBoundary(React, zl)
// <Boundary fallback={<p>Something went wrong.</p>}><App /></Boundary>
Every event carries url and userAgent; render errors include the React
component stack; a keepalive flush on pagehide means events survive tab
closes. Use a dedicated API key for browser traffic so it can be revoked independently.
Frontend-to-backend tracing
zl.instrumentFetch() // same-origin by default; propagateTo: ['https://api.you.com'] for more
This wraps fetch: every call to your backend gets a W3C traceparent
header (your OpenTelemetry-instrumented server continues the same trace), a browser-side root
span is exported so the waterfall starts in the user's browser, and failed requests are logged
with the trace id. Result: a user hits an error → one click from that log to the full
browser → backend → database waterfall on the Traces page.
Fluent Bit / Vector
For apps you can't modify, ship files and container output with a log shipper; both talk to the plain NDJSON endpoint.
Fluent Bit
[INPUT]
Name tail
Path /var/log/app/*.log
Tag app
# the tail input emits "log"; ZipLogger expects "message"
[FILTER]
Name modify
Match *
Rename log message
[OUTPUT]
Name http
Match *
Host app.ziplogger.ai
Port 443
TLS On
URI /ingest/v1/logs
Format json_lines
Json_date_key timestamp
Json_date_format iso8601
Header X-Api-Key zk_...
Vector
[sources.app]
type = "file"
include = ["/var/log/app/*.log"]
[transforms.shape]
type = "remap"
inputs = ["app"]
source = '''
.severity = "info"
.source = "legacy-app"
'''
[sinks.ziplogger]
type = "http"
inputs = ["shape"]
uri = "https://app.ziplogger.ai/ingest/v1/logs"
encoding.codec = "json"
framing.method = "newline_delimited"
request.headers.X-Api-Key = "zk_..."
.message already;
Fluent Bit's tail input uses log, hence the rename filter. Unknown JSON keys are
ignored by the ingestion endpoint, so extra metadata is harmless.OpenTelemetry
ZipLogger exposes a native OTLP/HTTP logs receiver at /v1/logs: protobuf and JSON,
gzip supported, partial success per the OTLP spec. Any OTel SDK or Collector can export to it:
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=https://app.ziplogger.ai
OTEL_EXPORTER_OTLP_HEADERS=X-Api-Key=zk_...
Resource attributes map to first-class fields: service.name → source,
service.version → release, deployment.environment, host.name,
and exception.stacktrace → the regression engine. Trace and span IDs are preserved
as searchable hex fields.
REST API
Ingestion
curl -X POST https://app.ziplogger.ai/ingest/v1/logs \
-H "X-Api-Key: zk_..." \
-H "Content-Type: application/json" \
-d '{"message":"deploy finished","severity":"info","source":"ci"}'
Accepts a single object, a JSON array, or NDJSON (one object per line). Fields:
timestamp, source, severity, message,
release, commitSha, stackTrace, fields{},
tags[]; everything optional except message.
Search
GET /api/v1/logs/search?q=payment+failed&severity=error&from=2026-07-01T00:00:00Z
Authorization: Bearer <jwt>
Additional endpoint groups: /api/v1/logs/histogram, /api/v1/templates
(patterns), /api/v1/dashboards, /api/v1/alerts,
/api/v1/metrics, /api/v1/regressions, and /api/v1/billing.
Quotas
When a daily or monthly quota is exhausted, ingestion answers 429 with a
Retry-After header pointing at the next UTC midnight, plus a JSON body with your
current usage. The official SDKs honor it automatically.
Authentication
Two credential types, used for different things:
| Credential | Used for | How |
|---|---|---|
API key (zk_…) | Log & metric ingestion, OTLP | X-Api-Key header |
| JWT | Everything else (search, dashboards, billing…) | Authorization: Bearer |
Sign in via POST /api/v1/auth/login to receive a short-lived access token and a
rotating refresh token; refresh with POST /api/v1/auth/refresh. Roles (Admin, Editor,
Viewer) gate destructive and administrative operations.
API keys
Create keys under Settings → API keys (or POST /api/v1/admin/api-keys). The
full key is shown exactly once at creation; only a SHA-256 hash is stored. Each key belongs to one
tenant, counts toward your plan's key limit, and can be revoked instantly. Rotate by creating a
new key, deploying it, then revoking the old one: zero downtime.
Configuration
Day-to-day configuration happens in the app, per workspace:
| Setting | Where |
|---|---|
| Ingestion API keys (create / rotate / revoke) | Settings → API keys |
| Team members and roles | Team |
| Plan, usage, invoices, payment method | Billing |
| Alert rules, channels (email / webhook / SMS / call / script) | Alerts |
| Twilio account for SMS & voice alerts | Settings → Alert notifications |
| Connected GitHub repositories for regression detection | Settings → Repositories |
| Your AI provider key (Claude / OpenAI / Gemini) | Settings → AI analysis |
| Per-service health-check URLs | Dashboards → Services → ⚙ |
Infrastructure (TLS, backups, capacity, upgrades) is our job, not yours: there are no server-level settings to manage.
Custom dashboards All plans
One dashboard for the whole picture. A ZipLogger dashboard is not a logs dashboard with charts bolted on: every part of the product supplies widgets to the same board, so a single screen can hold your error rate, your checkout funnel, active alerts, service health, the last deployment and what AI makes of it all.
Build one under Dashboards → + New, then + Widget. The catalog is grouped by where the data comes from:
- Events — event trend (grouped by service, country, device or version), event and user counts, sessions, top events, funnels, users by country/device/browser, numeric property totals, and event → errors: how often the requests carrying an event fail.
- Errors & logs — error count, error rate, error trend, top error patterns, log volume, and breakdowns by severity or service.
- Alerts — what is firing right now, and recent firings across every rule.
- Services & health — service status with health-check results, p95 response time, request volume, trace error rate.
- Deployments & Git — recent releases, suspected regressions with their suspect commit, and release impact: error rate before and after the latest deploy.
- AI — a written production summary, using your own AI key.
Widgets can be dragged to reorder, resized between one and four columns wide, duplicated and configured. The layout is saved on the server, not in your browser, so it is the same on every machine you sign in from.
A time range and filters at the top apply to every widget that understands the dimension — environment, service, country, version, or one specific end user. A widget can narrow further in its own settings, and where both set the same dimension the widget wins. Useful filter combinations can be saved by name and reused across dashboards.
Dashboards are shared with your team by default or can be made private, which is enforced on the server: a private dashboard is not reachable by another member even with its URL. Each dashboard has a stable link you can paste into an incident channel, and you can keep as many as you like — Production overview, Checkout, API health, Customer support.
Alerts & notifications
An alert rule counts matching events over a sliding window and fires on a condition: count > threshold, count < threshold, no data (nothing matched at all, a service went quiet), service down (a health check is failing, see below), or API latency increase, or API traffic anomalies. The latency condition is powered by traces: it fires when an operation's p95 latency rises a chosen percentage above its own 24-hour baseline (for example +50%), per service or per route. Traffic rules catch retry storms, runaway loops, and abuse: an absolute cap ("more than 500 requests to /api/orders in 1 minute") or a relative one ("3x normal traffic"), optionally narrowed by service, endpoint, or any span attribute (method, path, status code, user agent), each with a triage severity. Alerts fire once when the threshold is crossed and once on recovery, so there are no notification storms while it stays exceeded. Log-count rules can filter by severity and by the same query syntax as Search.
When a rule fires (and when it recovers) it notifies every channel you configured:
| Channel | Notes |
|---|---|
| One or more comma-separated addresses. Works out of the box. | |
| Webhook | HTTP POST with a Slack-compatible body ({"text": "…"}): Slack, Discord, Mattermost, or your own endpoint. |
| SMS / voice call | Works with no setup through ZipLogger's built-in sender, or with your own Twilio account (connect the Account SID, Auth Token, and a From number under Settings → Alert notifications; verified on save, token encrypted at rest). Calls ring only when the alert fires and read it aloud. Message text is capped at 150 characters. |
| Custom script | Sandboxed JavaScript run on every state change: page PagerDuty, open a ticket, call any API. The script sees alert, secrets.NAME (encrypted, never displayed again), http.post/get to public URLs, and log(); a built-in test runner dry-runs it against a fake alert. Limits: 5s, five HTTP requests. |
A custom message text on the rule (runbook link, who to wake) is included in every channel.
What notifications cost
Phone and email alerts work immediately, with a free monthly allowance on every plan: 20 voice calls, 50 SMS, and 100 emails per workspace per month. Past the allowance, sends draw from prepaid credits at $0.25 per call, $0.10 per SMS, and $0.02 per email. Top up in $5, $10, or $25 packs under Settings → Alert notifications, where you can also see the month's usage and your balance. Webhooks and custom scripts are always free and unlimited.
Connect your own Twilio account and none of this applies: SMS, calls, and emails all become unmetered, billed by Twilio at your own rates, with no ZipLogger notification charges. If the allowance runs out and no credits remain, the alert is still recorded in ZipLogger and its other channels still fire; only the metered send is skipped.
Service health checks
The Dashboards page lists every service seen in your telemetry over the last 24 hours with a derived status: healthy, errors (error-level events in the last 15 minutes), or silent (no telemetry for 15+ minutes). A quiet service isn't necessarily a dead one, so any card's ⚙ lets you add an active health check: a public URL ZipLogger requests on your chosen interval (1–60 min). Any response below HTTP 400 counts as up (shown with latency); an error status or timeout marks the service down on the dashboard. Tick “Alert when this service goes down” to create a linked service down alert rule with any of the notification channels above.
Metrics / APM
Install ZipLogger.Metrics.AspNetCore (NuGet) and add the middleware; every request's
duration is batched and shipped with your API key. The Metrics page charts average and p95
latency plus throughput per service; other stacks can POST the same shape to
/ingest/v1/metrics.
Distributed traces
ZipLogger accepts OpenTelemetry traces on the standard OTLP/HTTP path (/v1/traces,
protobuf or JSON, gzip supported), authenticated with the same API key as logs. No collector is
required; point your service's exporter straight at ZipLogger:
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=https://app.ziplogger.ai
OTEL_EXPORTER_OTLP_HEADERS=X-Api-Key=zk_...
The Traces page lists recent requests (filter by service, time, or errors only). Opening one shows the waterfall: every operation the request triggered, nested under its parent, colored by service, with durations, error spans in red, and each span's attributes one click away. Error logs that carry a trace id show a View trace link, so you can jump from a log line to the full request flow that produced it. Spans count toward your plan's log quota. Retention: traces that contain an error are kept for your plan's full retention; error-free traces are cleaned up after 48 hours.
Product analytics All plans
Events are things users did — checkout_started, product_viewed,
signup_completed — stored next to your logs and traces and joined on the same
correlation ids. That join is the feature: on any error pattern in Search, one click on
"What were users doing?" reconstructs the journey that led into the error
(product_viewed → add_to_cart → checkout_started → ❌), how many users were affected,
and where they are. No second analytics vendor, no id-stitching project.
From .NET it is one call on the SDK you already have
(ZipLogger.Extensions.Logging 0.5.0+ registers it for you — inject
IEventTracker; or use ZipLogger.Client directly):
analytics.Track("checkout_started", new { userId = cart.UserId, amount = cart.Total });
analytics.Identify(userId, anonymousId); // link pre-login events after sign-in
Tracking is buffered, batched, and retried in the background, and never blocks or throws in your
request path. Any other stack can POST /ingest/v1/events with the same API key as
logs — single object, array, or NDJSON.
The Events page has the dashboard (volume over time with deployment markers, unique users,
sessions, countries, devices, top events) and an explorer with cursor paging. Every user gets a
profile assembled from their events; every session gets a journey view with the request's errors
interleaved; and each event name gets its own page with volume, failure-rate correlation, and
numeric property analysis (sum/avg/min/max of e.g. amount).
Privacy is default-on: raw IPs are never stored (location is an approximate in-process lookup,
and only if the operator installs a GeoIP database), User-Agent strings are classified then
discarded, URLs lose their query strings, and credential-shaped properties — keys like
password, values that look like API keys, JWTs, or card numbers — are redacted at
ingest. Daily event volume is per plan, enforced with 429 + Retry-After; over-quota
batches are partially accepted, never dropped wholesale, and your app never sees an exception.
Already using Mixpanel? ZipLogger accepts Mixpanel-shaped payloads, so migrating is
usually two lines of configuration: point your SDK's API host at
/ingest/v1/mp and swap the project token for a ZipLogger ingest key. Your event
names, properties, identify calls and batching keep working as written, and
/import takes your historical export. See the
migration guide.
Browser automations All plans
Logs tell you a request failed. They do not tell you that your sign-in button stopped working. A browser automation does: under Automation → New automation, enter the page your journey starts on and a real Chromium opens in your browser window. Sign in, search, add to basket, check out. Every click, keystroke and selection is captured as a replayable step. Press Stop recording, name it, and ZipLogger replays the whole journey on a schedule, from every minute to once a day.
Nothing to install: the browser runs on our infrastructure and streams to your tab, so there is no extension, no download, and no test framework to learn. Passwords are captured as a reference, never as text. You supply the value once when you save the automation, and it is encrypted at rest and masked out of every screenshot, error message and log line afterwards.
When a step stops working you get the screenshot of the page at the moment it broke, the console errors, the failed network requests, page-load timing, and the step it stopped on. A failure only counts once every retry has failed, so a momentary blip does not wake anyone. Real failures open an alert on your Alerts page and notify by email, Slack, Microsoft Teams or webhook. With an AI key configured you also get a plain-English explanation of the likely cause, and if you have connected a repository, the commits that landed since that journey last passed, usually the fastest route from "checkout is broken" to "it was this commit".
Each automation records its success rate and response time over the last 30 days, so a journey that still passes but has doubled in duration is visible before it fails. Recorded steps carry several ways to find each element (test id, accessible role and name, label, text, then a CSS path), so an ordinary front-end change does not break the automation, and a step that has fallen back to its last-resort selector is flagged before it does. When that happens, the run also proposes a replacement derived from the element as it exists now, for you to apply in one click: suggested, never applied automatically, because a selector rewritten unattended is an automation that quietly starts checking the wrong button.
It knows what your server did. Every run carries a trace id, so if your application sends traces to ZipLogger the failing request is already filed under it: the run page shows the endpoint that threw and its message, and when a stack trace is present it opens a regression case with ranked suspect commits from blame. No configuration on your side, because the ids line up by construction.
Broken and flaky are not the same thing. A step that fails every run is broken; a step that fails one run in thirty is flaky: a slow page, an animation racing a click, an intermittent third-party widget. Paging for it is how teams learn to ignore the channel. Every automation shows per-step pass rate and timing across recent runs, and a failure notification says outright when the step it stopped on has been intermittent.
API steps. A journey can also call an endpoint directly, from inside the browser's own
session, so it goes out as the signed-in user, with the cookies the flow has already earned.
"The logged-in customer's cart endpoint returns their cart" becomes one step of the journey
instead of a second monitor with its own copy of the login. Set the method, headers, body and the
statuses you accept (201, 2xx, 200-204); tokens go in as
encrypted secret references, like passwords.
Run it from where it matters. An automation can be pinned to a region, so "check the checkout from Frankfurt" means Frankfurt. And for an application that is not on the public internet (an intranet, a staging environment behind a VPN, an internal admin tool), you can run a private agent: one container inside your own network, enrolled from the Automation page with a single token. It pulls work outbound over HTTPS, so you open no ports and change no inbound firewall rules, and recording works exactly as it does for a public site.
Every plan includes automations: 1 on Free, 3 on Pro, 10 on Team, 25 on Business. Each run is given up to one minute before it is stopped and recorded as failed.
Git regression detection
Under Settings → Repositories, the fastest path is the GitHub App: click "Connect GitHub", install the app on your org or account, and pick repositories: no tokens to create, access is minted on demand and revocable from GitHub in one click. Alternatively, connect a GitHub repository with a fine-grained personal access token (Contents: Read-only), or a GitLab project (gitlab.com or self-managed) with an access token carrying read_repository. Nothing is cloned either way: when you run “Find the commit that caused this” on an error pattern, ZipLogger parses the stack trace and uses the GitHub API to blame only those files, ranking the most likely commits with author and diff context. The token is encrypted at rest; an optional AI pass writes a root-cause narrative and a suggested fix.
AI analysis: bring your own key
AI features run on your AI account: paste an Anthropic Claude, OpenAI, or Gemini API key under Settings → AI analysis (verified on save, encrypted at rest, never shown again; the model is optional). That unlocks plain-English search (“payment errors in the last 2 hours”), one-click error summaries, and regression root-cause analysis, metered by your plan's monthly AI requests. Only a bounded sample of the logs you explicitly analyze is sent (never anything in the background), and without a key every other feature works normally.
MCP server: debug from your AI assistant
ZipLogger is an MCP (Model Context Protocol) server, so AI coding assistants like Claude Code and Cursor can query your workspace directly: ask "why is checkout failing in prod?" and the assistant searches your logs, clusters error patterns, walks trace waterfalls, and reads regression analyses by itself. Read-only, authenticated with a workspace API key.
# Claude Code
claude mcp add ziplogger --transport http https://app.ziplogger.ai/mcp \
--header "X-Api-Key: zk_..."
Tools exposed: search_logs, list_error_patterns, list_traces,
get_trace, get_services_status, list_regressions.
FAQ
Logs aren't showing up. What should I check?
1) The API key is sent as X-Api-Key and hasn't been revoked. 2) The endpoint is
reachable from your app (the SDKs retry silently; check DroppedCount).
3) You haven't exhausted your daily quota; the Billing page shows today's usage. 4) The
ingestion response: 202 accepted, 429 quota, 401 bad key.
Can I import logs with historical timestamps?
Yes, set timestamp on each event. They land in the right time buckets, count
toward today's ingestion quota, and age out based on their own timestamp.
How do I connect a repository for regression detection?
Under Settings → Repositories, connect your GitHub repo with a fine-grained access token (Contents: Read-only, stored encrypted). Nothing is cloned; blame runs via the GitHub API on the files in the stack trace, on demand. See Git regressions.
Where does the AI send my data?
Only to the provider whose key you configured (Anthropic Claude, OpenAI, or Gemini), only when you invoke an AI feature, and only a bounded sample of the window you're analyzing. No key configured → the features hide themselves. See AI analysis.