boltZharp Docs
hubApp Telemetry

OpenTelemetry

Zharp takes OpenTelemetry traces and logs over OTLP/HTTP. Any language with an OpenTelemetry SDK, and any Collector, can send to it without a Zharp SDK.

The Zharp SDKs are themselves OpenTelemetry underneath, so this page is also the reference for what they send. To set up an app, add it in App Telemetry with the OpenTelemetry platform: the setup page fills in the key and service name for you.

Endpoint and headers

Value
Base endpointhttps://api.zharp.io/api/v1/otlp
TracesPOST /v1/traces under it
LogsPOST /v1/logs under it
ProtocolOTLP/HTTP, as protobuf (application/x-protobuf) or JSON (application/json). gRPC isn't accepted directly — send gRPC to the agent or a Collector.
Compressiongzip or deflate, as Content-Encoding
SizeUp to 10 MB per request, after decompression
KeyAuthorization: Bearer <key> — or an x-zharp-key or x-api-key header

Metrics aren't taken here. The key is an app key (zpk_…) or a workspace API key that can ingest; see Keys for the difference.

Any OpenTelemetry SDK

Every OpenTelemetry SDK reads these environment variables. Traces and logs both go over OTLP/HTTP:

OTEL_EXPORTER_OTLP_ENDPOINT=https://api.zharp.io/api/v1/otlp
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer zpk_…
OTEL_SERVICE_NAME=checkout-api
OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production

The exporter adds /v1/traces and /v1/logs to the endpoint itself. Some SDKs default to gRPC; set the protocol:

OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf   # or http/json — not grpc

Then turn on your language's instrumentations for your framework, HTTP client and database. To link the app to a Zharp front end, keep the default propagators — tracecontext and baggage — and copy enduser.id and session.id from baggage onto your spans and logs (OpenTelemetry's baggage span processor does this where your language has one).

OpenTelemetry Collector

Add an exporter, then add it to your traces and logs pipelines:

exporters:
  otlphttp/zharp:
    endpoint: https://api.zharp.io/api/v1/otlp
    headers:
      Authorization: Bearer zpk_…

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/zharp]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/zharp]

A Collector usually sends for several services. Use a workspace API key with Ingest only ticked, and each service's service.name picks its app — new ones appear as discovered apps. With an app key, everything goes to that one app.

Attributes Zharp reads

Attributes are read from the record first, then its resource, and follow the OpenTelemetry semantic conventions. Everything else you send is kept and shown with the span or log.

AttributeUsed for
service.nameWhich app it belongs to, when the key is a workspace key. OpenTelemetry's unknown_service default counts as none.
service.versionThe release.
deployment.environment.name (or deployment.environment)The environment.
enduser.id (or user.id)The user: the Users tab and who an error happened to.
session.idThe session the record belongs to.
browser.name, browser.versionThe browser.
os.name (or os.type), os.versionThe operating system.
device.id, device.model.name (or device.model.identifier)The device.
telemetry.sdk.languageThe platform a discovered app is listed under.
http.request.method, http.route, http.response.status_codeRequests, routes and failures on the Overview and Traces tabs. The older http.method, http.target and http.status_code work too.
db.system.nameDatabase spans.

Requests are the spans of kind SERVER and the spans with no parent (a page load, a screen, a job); one with status ERROR is a failed request. Log levels come from severityNumber (9 info, 13 warn, 17 error, 21 fatal), or from severityText when the number is missing.

How errors are detected

An error event — the thing grouped into issues — is any of:

  • A span event named exception with exception.type, exception.message and exception.stacktrace — what span.recordException() writes in every OpenTelemetry SDK.
  • A log record with any of those exception.* attributes.
An exception span event, as OTLP/JSON
{
  "name": "exception",
  "attributes": [
    { "key": "exception.type", "value": { "stringValue": "PaymentError" } },
    { "key": "exception.message", "value": { "stringValue": "Card declined" } },
    { "key": "exception.stacktrace", "value": { "stringValue": "PaymentError: Card declined\n    at charge (src/pay.js:42:11)\n…" } },
    { "key": "zharp.handled", "value": { "stringValue": "true" } }
  ]
}
  • zharp.handled "true" marks one your code caught; anything else is shown as unhandled. OpenTelemetry's exception.escaped false means handled too.
  • Send the stack trace as the language prints it, with Type: message on the first line. Zharp recognises the frame formats of JavaScript, Python, Go, Java and Kotlin, PHP, Dart and Apple crash reports, and groups by your own frames.
  • Record the exception on the span it happened in, so the issue links to its trace.

Responses

StatusMeaningWhat an exporter does
200Stored—
400Not valid OTLP, or too largeDrops the batch
401Missing, invalid or revoked keyDrops the batch
403The key doesn't allow this origin or IP addressDrops the batch
503Zharp couldn't store it just nowRetries with backoff

Try it with curl

curl https://api.zharp.io/api/v1/otlp/v1/logs \
  -H "Authorization: Bearer zpk_…" \
  -H "Content-Type: application/json" \
  -d '{"resourceLogs":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"checkout-api"}}]},
       "scopeLogs":[{"logRecords":[{"severityNumber":9,"body":{"stringValue":"Hello from curl"}}]}]}]}'
lightbulb

This is what Send a test event on the setup page does. If it arrives and your app's data doesn't, the key is fine and the problem is in the exporter's configuration.