Documentation

Everything you need to go from zero to searchable logs. Ten minutes, tops.

Getting started

From zero to searchable logs in four steps:

  1. Create your free account and sign in (details below).
  2. Create an ingestion API key under Settings → API keys.
  3. Add a ZipLogger package to your app, or point your OTel exporter at it.
  4. 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).

All your apps need is outbound HTTPS to ZipLogger. There are no servers to size, no databases to back up, and no upgrades to schedule: we run the platform, you connect services and read answers.

.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_..."
Vector's file source puts the line in .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:

CredentialUsed forHow
API key (zk_…)Log & metric ingestion, OTLPX-Api-Key header
JWTEverything 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:

SettingWhere
Ingestion API keys (create / rotate / revoke)Settings → API keys
Team members and rolesTeam
Plan, usage, invoices, payment methodBilling
Alert rules, channels (email / webhook / SMS / call / script)Alerts
Twilio account for SMS & voice alertsSettings → Alert notifications
Connected GitHub repositories for regression detectionSettings → Repositories
Your AI provider key (Claude / OpenAI / Gemini)Settings → AI analysis
Per-service health-check URLsDashboards → 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:

ChannelNotes
EmailOne or more comma-separated addresses. Works out of the box.
WebhookHTTP POST with a Slack-compatible body ({"text": "…"}): Slack, Discord, Mattermost, or your own endpoint.
SMS / voice callWorks 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 scriptSandboxed 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 didcheckout_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.

Ready to send your first log?

Free plan, two-line setup, no card.

Start free