Receive metrics over OpenTelemetry (OTLP)
Run the agent as an OTLP/HTTP receiver so any OpenTelemetry-compatible sender can push metrics into Observer.
The agent runs an HTTP server on port 4318 that accepts OpenTelemetry metric pushes. Any OTLP-compatible sender (OpenTelemetry SDK, OpenTelemetry Collector, vendor agent with an OTLP exporter) writes metrics to it; each metric you configure in Observer becomes a probe you can threshold on.
This is the only push-mode source. Every other source type
(prometheus, http, tcp, etc.) pulls on a fixed interval. OTLP
inverts the direction: the sender drives.
Listener configuration
Set on the agent process:
# Bind address. Defaults to loopback so only same-host processes can
# push without an explicit token. Set to 0.0.0.0:4318 to accept pushes
# from other hosts; a bearer token becomes mandatory in that case.
OBSERVER_OTLP_LISTEN_ADDR=127.0.0.1:4318
# Required when binding a non-loopback address. The agent refuses to
# start otherwise.
OBSERVER_OTLP_BEARER_TOKEN=replace-with-a-32-byte-secret
# Max number of distinct streams (metric_name x attribute fingerprint)
# the receiver keeps in memory. Defaults to 1000.
OBSERVER_OTLP_MAX_BUFFER_POINTS=1000
# Skip the receiver entirely. Useful if you only run pull probes.
OBSERVER_OTLP_DISABLE=true
The receiver starts lazily the first time a metric definition with
source_type: otlp is dispatched. If no such metric is configured,
no port is opened.
When NOT to use this
- You already run Prometheus and the metric is exposed there. Use
the
prometheussource instead. It doesn't require wiring up a push pipeline and the agent doesn't need to bind a port. - You want server-side aggregation across multiple senders. The
receiver records one data point per
(metric_name, attribute fingerprint)stream. It does not merge samples across senders. Aggregate at your OpenTelemetry Collector with agroupbyattrsortransformprocessor before exporting. - You can't bound the cardinality of your attributes. The receiver caps streams at 1000 by default. A metric with unbounded user IDs or request IDs in its attributes will keep evicting itself. Strip high-cardinality attributes at the source.
Configure a metric in the Observer console
Create a new metric, pick OpenTelemetry (OTLP) from the source picker, and fill in:
- Metric name: the OTLP
metric.namefield your sender emits (http.server.duration,queue.depth, etc.). Exact match. - Attribute filters (optional): one
key=valueper line. Only data points carrying every key=value pair are read. Leave blank to read whichever matching data point is most recent. - Aggregation: how to collapse the data point into a single number. See Aggregation reference.
- Staleness window: when the most recent matching sample is older than this, the probe surfaces
no_datawith reasonotlp_stale. Default 120 seconds.
The threshold fields work the same as any other source: the aggregation produces a scalar; healthy and unhealthy bounds map that scalar to status.
Aggregation reference
| Aggregation | Gauge / Sum | Histogram | Exponential histogram |
|---|---|---|---|
latest | the data point value | mean (sum / count) | mean |
count | the data point value | the bucket count total | the count |
sum | the data point value | the sum | the sum |
mean | the data point value | sum / count | sum / count |
p50 / p95 / p99 | the data point value | linear interpolation across the bucket containing the target rank | linear interpolation across the positive / negative / zero buckets |
For gauges and sums the aggregation field is effectively ignored
because there is only one scalar to read. Pick latest to be
explicit.
Sending from the OpenTelemetry Collector
Add an OTLP/HTTP exporter to your collector config:
exporters:
otlphttp/observer:
endpoint: http://<agent-host>:4318
encoding: json
# Required only if the agent has OBSERVER_OTLP_BEARER_TOKEN set.
headers:
Authorization: "Bearer <your-token>"
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/observer]
encoding: json is the v1 requirement. The default for otlphttp is
protobuf; the agent rejects that with HTTP 415 so the collector logs
make the cause obvious.
Sending from an application
The OpenTelemetry SDKs ship OTLP exporters in every language binding. JavaScript example:
const exporter = new OTLPMetricExporter({
url: "http://<agent-host>:4318/v1/metrics",
headers: { Authorization: "Bearer <your-token>" },
});
Equivalent SDKs exist for Python, Go, Java, .NET, and Ruby. Each
follows the same OTLP_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS
environment variable contract.
Reason codes specific to OTLP
The reason field on no_data results carries one of:
otlp_no_samples: nothing has arrived with this metric name and attribute filter since the agent started. Verify the sender is pointed at the right port andmetric_namematches exactly.otlp_stale: the most recent matching sample is older than the configured staleness window. The sender has stopped pushing, or the metric was renamed at the source.otlp_aggregation_unavailable: the chosen aggregation is not extractable from the data point shape (e.g. a non-finite bucket base in a malformed exponential histogram). Switch tomean,count, orsumand verify the sender's bucket geometry.otlp_receiver_disabled: the agent was started withOBSERVER_OTLP_DISABLE=true. Unset that variable and restart.push_source_init_failed: the receiver could not start. Common cause: another process already bound the configured port. Free the port or changeOBSERVER_OTLP_LISTEN_ADDR.
Security
By default the receiver binds 127.0.0.1 only. Other processes on
the same host can push metrics; the network cannot. The agent refuses
to start with a non-loopback bind unless OBSERVER_OTLP_BEARER_TOKEN
is set.
Authentication is a constant-time bearer-token compare against the
Authorization: Bearer <token> header. Generate the token with 32
bytes of OS randomness (openssl rand -hex 32). Rotate by editing
the env var and restarting the agent; the sender must update at the
same time.
The receiver does not inspect TLS. If you need transport encryption across a network, terminate TLS at a sidecar reverse proxy and have the proxy forward to the loopback receiver. mTLS at the agent is a follow-up item.
Backpressure and limits
The receiver buffers the most recent data point per stream
(metric_name × attribute fingerprint). When the stream count
exceeds OBSERVER_OTLP_MAX_BUFFER_POINTS, the oldest stream is
dropped and a counter on the agent dashboard increments. There is no
queue between the receiver and the buffer: each accepted request
writes synchronously. Senders are never blocked.
For high-cardinality metric streams, prefer fewer distinct attribute combinations per metric or split into multiple metric names. The cap exists to bound memory growth on stuck-tag-explosion incidents.
Known limits
These are intentional design decisions, surfaced here so you know which workloads to route elsewhere.
- OTLP/gRPC on port 4317 is not implemented. The receiver returns
HTTP 415 on
application/x-protobuf. Configure your sender for OTLP/HTTP JSON. - OTLP traces and logs are not accepted. Observer's domain is status, and metrics drive status. Traces and logs go to a tracing / logging backend.
- Exponential-histogram quantile estimation is geometry-based.
The agent walks the
positive,negative, and zero buckets in ascending numeric order usingbase = 2 ^ (2 ^ -scale)and interpolates within the target bucket. Accuracy depends on the sender'sscale(higher scale = finer buckets = tighter estimate). Senders that emit only a few buckets per decade trade storage for precision; pickmeanif the rough centre is enough.
Troubleshooting
Each entry leads with the symptom in bold and the action to take.
otlp_no_sampleswhile the collector logs successful exports. Verify the metric name is byte-for-byte identical between your sender and the Observer metric definition. OTLP metric names are case-sensitive and.is significant. The collector'sdebugexporter (setverbosity: detailed) prints the names it received.- Sender gets
415 Unsupported Media Type. Your exporter is using protobuf. Setencoding: jsonon theotlphttpexporter (collector) or send the SDK to the JSON exporter package. - Sender gets
Connection refused. The receiver only opens its port after the first OTLP metric definition is dispatched. In the console, create a metric with source type OpenTelemetry (OTLP) and save it, then retry the send within five seconds. - Receiver runs but no metrics show up in Observer. From the
agent host:
ss -tlnp | grep 4318should list the agent process bound to the configured address. If it lists127.0.0.1, a sender on another host can't reach it. SetOBSERVER_OTLP_LISTEN_ADDR=0.0.0.0:4318, setOBSERVER_OTLP_BEARER_TOKEN, restart the agent, and addAuthorization: Bearer <token>to the sender. otlp_aggregation_unavailableon a quantile (p50/p95/p99). Most often a malformed exponential histogram: the sender shipped ascaleoutside [-10, 20], or the bucket geometry produced non-finite bounds. Verify the sender's exporter configuration and thescaleit picks; switch tomean,count, orsumif the geometry is fixed at the source.