Skip to content

Custom metrics with OpenTelemetry

Auto-instrumentation gives you request rate, error rate, and latency for free. Custom metrics are for the numbers that are specific to your application — payments processed, items in a queue, cache hit ratio, model inference time. You define them with the OpenTelemetry metrics API and they arrive in Osuite over the same OTLP exporter as your traces and logs.

You will use three instruments, one per metric type:

  • Counter — a value you add to as events happen (add(1)). Only goes up.
  • Histogram — a value you record per event (record(value)); the SDK buckets the distribution so you can compute percentiles later.
  • Observable gauge — a value the SDK reads on each export by calling a callback you provide. Use it for a current level you can measure on demand, such as queue depth or connection count.

Get a meter, create your instruments once at startup, then record against them from your application code.

import { metrics } from '@opentelemetry/api';
const meter = metrics.getMeter('payment-service');
const paymentsProcessed = meter.createCounter('payments.processed', {
description: 'Number of payments processed',
});
const paymentAmount = meter.createHistogram('payment.amount', {
description: 'Distribution of payment amounts',
unit: 'USD',
});
const queueDepth = meter.createObservableGauge('payment.queue.depth', {
description: 'Payments awaiting processing',
});
queueDepth.addCallback((result) => {
result.observe(getQueueDepth());
});
export function onPayment(amount, currency) {
paymentsProcessed.add(1, { currency });
paymentAmount.record(amount, { currency });
}

Custom metrics use the same OTLP exporter as the rest of your telemetry. Set these environment variables wherever the service runs — they configure the exporter for traces, logs, and metrics alike:

Terminal window
OTEL_SERVICE_NAME="payment-service"
OTEL_RESOURCE_ATTRIBUTES="service.environment=production"
OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.osuite.io:443"
OTEL_EXPORTER_OTLP_HEADERS="x-osuite-ingest-token=<your-ingest-token>"

service.environment is the key Osuite reads to separate telemetry across production, staging, and development — set it per environment in your deploy. Use service.environment, not the standard OTel deployment.environment, which Osuite does not read.

Osuite stores metrics under Prometheus-normalized names, so the name you query is not always the name you declared:

  • Dots become underscores: payments.processed is queried as payments_processed.
  • Counters gain a _total suffix: payments.processed becomes payments_processed_total.
  • A histogram named payment.amount becomes three series — payment_amount_bucket, payment_amount_sum, and payment_amount_count.

See Querying with PromQL for how to use each of these.

What you should see

Within a minute of your service recording its first data point, the new metrics appear in the Explore metrics view under their normalized names, with the currency label available to filter and group by.

Custom metrics are only emitted while your application is calling add / record (synchronous instruments) or while the export callback is firing (observable gauge). A metric that is never exercised will not appear.

No data in Osuite after a few minutes? Work through these checks.

  • Endpoint — confirm the exporter targets exactly ingest.<region>.osuite.io:443 for your region, over TLS.
  • Token — confirm the ingest header carries a valid <your-ingest-token> and has not been rotated.
  • Pipeline — confirm the signal you expect (traces, logs, or metrics) is wired into an active pipeline with the otlp/osuite exporter attached.
  • Export errors — check the application or Collector logs for OTLP export failures (auth, DNS, TLS, connection refused).
  • Network — confirm the host has outbound access to the ingest endpoint on port 443.
  • Timing — allow up to a minute for the first data to appear before assuming a failure.

Still stuck? Ask the Investigation Agent or contact support.