Tags & parsing
Every line is filed under a service, and can carry an environment, a version and a source. The source decides how Zharp reads fields out of the line.
Unified service tagging
Three tags tie a log line, a metric and a trace from the same deployment together: service, env and version. Logs also carry a source — the technology that wrote them (nginx, postgresql, java, json …). In Zharp they're the OpenTelemetry resource attributes service.name, deployment.environment.name (the older deployment.environment is read too), service.version and zharp.source. Log monitors can be scoped to an environment and a source, and the API's log query filters on all four.
| Where | Service | Environment | Version | Source |
|---|---|---|---|---|
| Environment variables (container or sidecar) | ZHARP_SERVICE | ZHARP_ENV | ZHARP_VERSION | ZHARP_SOURCE (sidecar) |
| Docker labels | zharp.service | zharp.env | zharp.version | zharp.source |
| Kubernetes pod annotations | zharp.io/service | zharp.io/env | zharp.io/version | zharp.io/source |
| Zharp SDK options | service | environment | release | — |
| OpenTelemetry resource | service.name | deployment.environment.name | service.version | zharp.source |
| HTTP API | service | env / environment | version | source |
A container's labels — or the ZHARP_* variables set on it — name its logs; without them the agent uses the Compose service (or container name) and, for well-known images such as nginx, postgres, redis or mongo, sets the source from the image. In Kubernetes, annotate the pod; app.kubernetes.io/name and app.kubernetes.io/version are the fallbacks, and a pod with several containers can tag each one with zharp.io/<container>.env and so on.
services:
checkout:
image: ghcr.io/acme/checkout:1.4.2
labels:
zharp.service: checkout
zharp.env: prod
zharp.version: "1.4.2"
zharp.source: nodemetadata:
annotations:
zharp.io/service: checkout
zharp.io/env: prod
zharp.io/version: "1.4.2"
zharp.io/source: node
# one container of several: zharp.io/<container>.service, .env, .version, .sourceAn app instrumented with OpenTelemetry sets the same tags as resource attributes, and they reach logs, metrics and traces alike:
OTEL_RESOURCE_ATTRIBUTES="service.name=checkout,deployment.environment.name=prod,service.version=1.4.2"
Untagged lines still arrive; tags are never required. Tags are stored as sent (up to 128 characters), so prod and production are two environments — pick one spelling.
Parsed formats
When a line arrives, its source picks a parser; a line without one is recognised by its shape. What the parser finds is stored beside the message — the message itself is never changed — and is included in the log's metadata. Search on it from the Logs page with an @, as in @http.status_code>=500. When the line states a level, that level is kept: a 5xx in an access log is error, a 4xx warn, a JSON level field is read as is. A level you send explicitly (anything other than info) wins over the line.
| Source | Recognised | Fields |
|---|---|---|
nginx | Access log (combined / common, with $request_time or rt=/urt= appended) and error log | http.method http.path http.status_code bytes client.ip http.user_agent http.referer duration_ms |
apache | Access log (common / combined, %D appended) and error log (2.2 and 2.4) | the access-log fields, plus apache.module error.code process.pid on error lines |
postgresql | Default log_line_prefix (%m [%p]) and Debian's %m [%p] %q%u@%d | duration_ms db.statement db.user db.name error.message process.pid |
mysql | Error log, 5.5 to 8.x | error.code (MY-…) mysql.subsystem mysql.thread_id error.message |
redis | Server log (pid:role date level message), and pre-3.0 | redis.role process.pid; level from . - * # |
mongodb | Structured JSON log (4.4+) | mongodb.component duration_ms (slow queries) db.name db.collection client.ip attr.* |
json | Any line that is one JSON object (pino, bunyan, winston, zap, structlog …) | every field flattened to dotted keys, with http.* duration_ms error.* trace_id taken from their common names |
logfmt | Lines of key=value pairs (logrus, Heroku router …) | every pair, with the same common names mapped |
java | Spring Boot and Logback/Log4j default patterns; stack traces | logger.name thread.name error.type error.message error.stack |
python | The logging module's common formats; tracebacks | logger.name error.type error.message error.stack |
node, go | JSON or logfmt first; then uncaught errors with a stack, Go panics and the standard logger | error.type error.message error.stack |
Lines over 16 KB aren't parsed (they're still stored), parsers read the first line of a multi-line message (stack traces are searched further), and a line keeps at most 64 parsed fields of up to 1,024 characters each. A detected format also sets the source when you didn't send one — except plain access logs, which nginx and Apache share.