This guide explains how to send custom metrics to Metoro using OpenTelemetry. Metoro supports the OpenTelemetry Protocol (OTLP) for metric ingestion, allowing you to send metrics from any application or service that can export OpenTelemetry metrics.

Prerequisites

  • A Metoro account
  • An application configured with OpenTelemetry

High Level Overview

The Metoro exporter running in each cluster is a fully compliant OpenTelemetry collector. This means that you can send metrics to Metoro using any OpenTelemetry compatible metrics library.

Endpoint Configuration

Configure your OpenTelemetry exporter to send metrics to:

http://metoro-exporter.metoro.svc.cluster.local/api/v1/custom/otel/metrics

This endpoint is available within your Kubernetes cluster where the Metoro exporter is installed.

Authentication

No additional authentication is required when sending metrics from within the cluster to the Metoro exporter.

Language-Specific Examples

Python

Here’s an example Python script that publishes deployment metrics to Metoro:

import sys

from kubernetes import client, config
from opentelemetry import metrics
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import (
    PeriodicExportingMetricReader,
)

# Main
if __name__ == "__main__":
    metric_reader = PeriodicExportingMetricReader(
        OTLPMetricExporter(endpoint="http://metoro-exporter.metoro.svc.cluster.local/api/v1/custom/otel/metrics")
    )
    provider = MeterProvider(metric_readers=[metric_reader])

    # Sets the global default meter provider
    metrics.set_meter_provider(provider)

    # Creates a meter from the global meter provider
    meter = metrics.get_meter(__name__)

    config.load_kube_config()

    namespace = sys.argv[1]
    app = client.AppsV1Api()

    deployment_data = app.list_namespaced_deployment(namespace)
    desired_replicas = meter.create_gauge("custom_metrics.desired_replicas")
    available_replicas = meter.create_gauge("custom_metrics.available_replicas")

    for item in deployment_data.items:
        desired_replicas.set(item.status.replicas, {"deployment": item.metadata.name})
        available_replicas.set(item.status.available_replicas, {"deployment": item.metadata.name})

    # Make sure that all the metrics are exported before the application exits
    provider.force_flush()

This script:

  1. Creates two gauge metrics:
    • custom_metrics.desired_replicas - The desired number of replicas for a deployment
    • custom_metrics.available_replicas - The number of available replicas for a deployment
  2. Includes a deployment attribute with the name of the deployment
  3. Uses the OpenTelemetry SDK to export metrics to Metoro

Go

package main

import (
    "context"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
    "go.opentelemetry.io/otel/sdk/metric"
    "go.opentelemetry.io/otel/sdk/resource"
    semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)

func initMetrics() (*metric.MeterProvider, error) {
    ctx := context.Background()

    exporter, err := otlpmetrichttp.New(ctx,
        otlpmetrichttp.WithEndpoint("metoro-exporter.metoro.svc.cluster.local"),
        otlpmetrichttp.WithURLPath("/api/v1/custom/otel/metrics"),
        otlpmetrichttp.WithInsecure(), // Since we're in-cluster
    )
    if err != nil {
        return nil, err
    }

    resource := resource.NewWithAttributes(
        semconv.SchemaURL,
        semconv.ServiceName("your-service-name"),
    )

    meterProvider := metric.NewMeterProvider(
        metric.WithReader(metric.NewPeriodicReader(exporter)),
        metric.WithResource(resource),
    )

    return meterProvider, nil
}

Node.js

const { MeterProvider } = require('@opentelemetry/sdk-metrics');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

const resource = new Resource({
  [SemanticResourceAttributes.SERVICE_NAME]: 'your-service-name',
});

const metricExporter = new OTLPMetricExporter({
  url: 'http://metoro-exporter.metoro.svc.cluster.local/api/v1/custom/otel/metrics',
  headers: {},
});

const meterProvider = new MeterProvider({
  resource: resource,
});

meterProvider.addMetricReader(new PeriodicExportingMetricReader({
  exporter: metricExporter,
  exportIntervalMillis: 1000,
}));

Metric Types

OpenTelemetry supports several metric types that you can use:

  1. Counter - A value that can only increase or be reset to zero
  2. Gauge - A value that can arbitrarily go up and down
  3. Histogram - Tracks the distribution of values over time

Attributes and Context

When sending metrics via OpenTelemetry, you can include additional attributes that will be indexed and searchable in Metoro:

  • Use resource attributes to define static information about the service
  • Use metric attributes to include dynamic information with each metric value
  • Link metrics with traces using trace context propagation

Troubleshooting

If you encounter issues with OpenTelemetry metric ingestion:

  1. Verify your endpoint URL is correct
  2. Check your network connectivity to the Metoro OTLP endpoint
  3. Enable debug logging in your OpenTelemetry SDK
  4. Verify your metrics appear in the Metoro metrics view
  5. Contact support if issues persist

Additional Resources