Already instrumented? Then you do not need a console client at all. Point a collector's logs pipeline at the console and error-level records become console errors — grouped, prioritised, alerted and browsable like everything else.
Before you start
You need the project's api_key and a running OpenTelemetry Collector (or an SDK that can export OTLP/HTTP directly).
1. Decide whether this is the right door
- Use OTLP if you already run a collector, or your language has an OTel SDK and no console client.
- Use a console client if you want batching at shutdown, redaction and automatic context without configuring a pipeline.
- Both at once is fine. They land in the same queue.
2. Know what the endpoint accepts
| endpoint | POST /api/v1/ingest/otel |
| auth | X-Console-Key: <api_key> |
| encoding | JSON only — protobuf answers 415 |
| signals | logs (errors), metrics (one rollup counter) |
| traces | answer 404 — export them to a tracing backend instead |
Why logs and not traces? A console is a place where a person decides what to fix. Traces are for a system that decides what is slow. Sending spans here would fill the grid with things nobody can act on.
3. Configure the exporter
Collector
exporters:
otlphttp/console:
logs_endpoint: https://yourconsole.cloud/api/v1/ingest/otel
encoding: json # protobuf is refused with 415
headers:
X-Console-Key: ${env:CONSOLE_KEY}
processors:
filter/errors: # send what a person should see
logs:
log_record:
- 'severity_number < SEVERITY_NUMBER_WARN'
batch:
send_batch_size: 200
timeout: 5s
service:
pipelines:
logs:
receivers: [otlp]
processors: [filter/errors, batch]
exporters: [otlphttp/console]SDK direct
Point the SDK's OTLP/HTTP log exporter at the endpoint:
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://yourconsole.cloud/api/v1/ingest/otel
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_LOGS_HEADERS=X-Console-Key=<api_key>
A collector in between is still worth it: it batches, filters and survives
your app restarting.Docker Compose
services:
collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel/config.yaml"]
environment:
CONSOLE_KEY: ${CONSOLE_KEY}
volumes:
- ./otel-config.yaml:/etc/otel/config.yaml:ro
ports:
- "4318:4318"4. Know what maps to what
| OTLP | console |
|---|---|
severityNumber / severityText | priority — FATAL→2, ERROR→3, WARN→4, INFO→6, DEBUG/TRACE→7 |
body | message (structured bodies are kept as JSON) |
exception.type / .message / .stacktrace | class, message, backtrace |
code.file.path / code.line.number | file and line — both feed the fingerprint |
service.name / service.version | host (the "application" column) and release |
url.* / http.* / client.address | request context: uri, method, ip, ua |
session.id / user.id | session and user columns |
traceId | trace id — one request's errors across every service |
| everything else | extra — span id, scope, severity, resource attributes |
5. Avoid the three things that trip people up
- Protobuf is refused. Set
encoding: jsonon the exporter or every export answers415. - Filter before exporting. Without the filter processor you will ship every INFO line your services emit and the priority gate will drop them — bandwidth spent to be ignored.
- Metrics are one counter. A monotonic DELTA sum named
console.rollup.requests, gated per project. Everything else in a metrics export is reported back throughpartialSuccessand never stored.
6. Check it worked
Send one WARN-level record and watch the grid.
| answer | meaning |
|---|---|
200 {} | accepted |
415 | the exporter is still sending protobuf |
404 | traces were pointed at a logs endpoint |
403 | metrics, on a project without rollups enabled |
Next: the wire-level detail is in Ingest API v1; the rules a client follows are in Build your own client.