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. Set up your ZipLogger workspace (below) and sign in.
  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.

Setting up your workspace

ZipLogger is self-hosted: it runs on your own server, so your logs never leave your infrastructure. Setup is a guided, single-command installation that takes about ten minutes on any modest cloud VM or on-prem machine — everything ZipLogger needs is included, and the step-by-step deployment guide ships with the product.

Once it's up, sign in with the administrator account created during setup, invite your team under Team, and pick a plan under Billing (the Free plan is active by default).

Server sizing, backups, upgrades, and reverse-proxy setup are covered in the operator deployment guide — your admin needs it once; everyone else just signs in.

.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://logs.yourcompany.com", "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://logs.yourcompany.com";
    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://logs.yourcompany.com",
    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://logs.yourcompany.com', 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/ahaliav/ziplogger/sdk_go
client, err := ziplogger.New(ziplogger.Options{
    Endpoint: "https://logs.yourcompany.com",
    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://logs.yourcompany.com", "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.

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             logs.yourcompany.com
    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://logs.yourcompany.com/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://logs.yourcompany.com
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://logs.yourcompany.com/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 and notification webhooksAlerts
Connected repositories for regression detectionRegressions
AI analysis (enabled per plan by your administrator)Search → AI

Server-level settings — TLS, backups, resource sizing, AI and payment credentials — are the administrator's one-time job and are covered in the operator deployment guide.

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 Regressions, add a name and the path of a repository clone on your ZipLogger server. Analysis runs entirely on that machine — your code never leaves your infrastructure.

Where does the AI send my data?

Only to the Anthropic API, 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.

Ready to send your first log?

Free plan, two-line setup, no card.

Start free