<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged otlp</title>
<link>https://coderlegion.com/tag/otlp</link>
<description>Powered by Question2Answer</description>
<item>
<title>Prometheus 3.0 and OpenTelemetry: Native OTLP Support Explained</title>
<link>https://coderlegion.com/15636/prometheus-3-0-and-opentelemetry-native-otlp-support-explained</link>
<description>
&lt;p&gt;Seven years is a long time in observability. Since Prometheus 2.0 landed in 2017, the ecosystem has been transformed by cloud-native adoption, the rise of distributed tracing, and the emergence of OpenTelemetry as the de facto standard for instrumentation. Prometheus 3.0, released in November 2024, is the project&amp;rsquo;s answer to that transformation &amp;mdash; and its most significant change is the native ability to ingest OpenTelemetry metrics directly, without an intermediary collector standing in the way.&lt;/p&gt;



&lt;p&gt;This article goes deep on what Prometheus 3.0 actually changes for platform engineers and cloud architects who are running &amp;mdash; or planning to run &amp;mdash; OTel-instrumented workloads alongside Prometheus-based monitoring stacks. We will cover the native OTLP ingestion endpoint, UTF-8 metric name support, Remote Write 2.0, migration considerations, and the architectural patterns that still make sense even when native OTLP is available.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Changed in Prometheus 3.0: The OTel-Relevant Picture&lt;/h2&gt;



&lt;p&gt;Prometheus 3.0 ships a substantial set of changes. Not all of them are equally relevant to OpenTelemetry integration, so let&amp;rsquo;s focus on what actually moves the needle for OTel users before diving into each area in detail.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Native OTLP Ingestion&lt;/h3&gt;



&lt;p&gt;The flagship feature: Prometheus 3.0 ships with a built-in OTLP receiver that exposes an HTTP endpoint accepting metrics in the OpenTelemetry Protocol format. Applications instrumented with any OTel SDK can now push metrics directly to Prometheus without routing through an OpenTelemetry Collector. This is not a sidecar, not a plugin, not an external adapter &amp;mdash; it is a first-class endpoint in the Prometheus binary itself.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;UTF-8 Metric Names&lt;/h3&gt;



&lt;p&gt;Prometheus historically restricted metric names to &lt;code&gt;[a-zA-Z_:][a-zA-Z0-9_:]*&lt;/code&gt;. OpenTelemetry uses dots and slashes in metric names by convention &amp;mdash; &lt;code&gt;http.server.request.duration&lt;/code&gt; is a canonical OTel metric name. Prometheus 3.0 lifts this restriction and supports arbitrary UTF-8 characters in metric names and label names, which is the single most important compatibility change for OTel interoperability.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Remote Write 2.0&lt;/h3&gt;

&lt;h3 class=&quot;wp-block-heading&quot;&gt;Remote Write 2.0&lt;/h3&gt;



&lt;p&gt;Remote Write 2.0 replaces the original protocol with a more efficient encoding based on protobuf, adds native histogram support in the wire format, and reduces bandwidth consumption significantly for large-scale deployments. If you are federating metrics to Thanos, Mimir, or Cortex, this matters for operational cost.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;New UI&lt;/h3&gt;



&lt;p&gt;The Prometheus web UI has been completely rewritten. The new UI uses React, supports metric metadata exploration, and provides a significantly improved query-building experience. This is a quality-of-life improvement rather than an architectural change, but it reduces the dependency on external tools like Grafana for ad-hoc investigation.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Breaking Changes Summary&lt;/h3&gt;



&lt;p&gt;Prometheus 3.0 removes several features that were deprecated in 2.x. The most operationally significant are: removal of the &lt;code&gt;--web.enable-admin-api&lt;/code&gt; deprecated flag path, removal of certain legacy storage format options, changes to default scrape timeouts, and stricter validation of configuration that was previously silently accepted. We cover a migration checklist later in this article.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The OTLP Receiver: How It Works and What It Accepts&lt;/h2&gt;



&lt;p&gt;The OTLP receiver in Prometheus 3.0 is implemented as an optional feature that must be explicitly enabled. Once enabled, it exposes an HTTP endpoint at &lt;code&gt;/api/v1/otlp/v1/metrics&lt;/code&gt; that accepts protobuf-encoded OTLP ExportMetricsServiceRequest payloads &amp;mdash; the same wire format used by the OpenTelemetry Collector&amp;rsquo;s OTLP exporter.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;What It Accepts (and What It Does Not)&lt;/h3&gt;



&lt;p&gt;This is critical to understand before you architect around native OTLP ingestion: &lt;strong&gt;Prometheus 3.0 OTLP support is metrics-only&lt;/strong&gt;. It does not accept traces or logs. OTLP is a unified protocol covering all three signals, but Prometheus is a metrics store &amp;mdash; the receiver handles only the metrics portion of the OTLP specification.&lt;/p&gt;



&lt;p&gt;Supported metric types in the OTLP receiver:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Gauge&lt;/strong&gt; &amp;mdash; maps directly to a Prometheus Gauge&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Sum (monotonic)&lt;/strong&gt; &amp;mdash; maps to a Prometheus Counter&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Sum (non-monotonic)&lt;/strong&gt; &amp;mdash; maps to a Prometheus Gauge&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Histogram (explicit bucket)&lt;/strong&gt; &amp;mdash; maps to a Prometheus Histogram&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ExponentialHistogram&lt;/strong&gt; &amp;mdash; maps to Prometheus Native Histograms (a 3.0 feature)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt; &amp;mdash; maps to a Prometheus Summary&lt;/li&gt;&lt;/ul&gt;



&lt;p&gt;Resource attributes from the OTLP payload &amp;mdash; things like &lt;code&gt;service.name&lt;/code&gt;, &lt;code&gt;k8s.pod.name&lt;/code&gt;, &lt;code&gt;cloud.region&lt;/code&gt; &amp;mdash; are converted to Prometheus labels. This conversion is configurable, and by default Prometheus applies a promotion strategy that converts the most common resource attributes to labels while discarding ones that would create extremely high cardinality.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Enabling the OTLP Receiver&lt;/h3&gt;



&lt;p&gt;Enabling native OTLP ingestion requires two things: a feature flag and a configuration block in &lt;code&gt;prometheus.yml&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;Start the Prometheus binary with the feature flag:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --enable-feature=otlp-write-receiver&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Then add the OTLP receiver configuration to your &lt;code&gt;prometheus.yml&lt;/code&gt;:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

otlp:
  # Promote these OTLP resource attributes to Prometheus labels
  promote_resource_attributes:
    - service.name
    - service.namespace
    - service.instance.id
    - k8s.namespace.name
    - k8s.pod.name
    - k8s.node.name
    - cloud.region
    - deployment.environment&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;With this configuration, Prometheus will listen on port 9090 (default) and accept OTLP metrics at &lt;code&gt;http://&amp;lt;prometheus-host&amp;gt;:9090/api/v1/otlp/v1/metrics&lt;/code&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Resource Attribute Promotion Strategy&lt;/h3&gt;



&lt;p&gt;The &lt;code&gt;promote_resource_attributes&lt;/code&gt; list deserves careful thought. OTLP carries rich resource-level context &amp;mdash; every metric payload includes a ResourceMetrics object with attributes describing the source: service name, version, environment, Kubernetes pod, node, cluster, cloud provider details, and more. Prometheus labels are flat key-value pairs on each time series. Promoting too many resource attributes explodes cardinality; promoting too few loses important context.&lt;/p&gt;



&lt;p&gt;A pragmatic starting list for Kubernetes deployments:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;otlp:
  promote_resource_attributes:
    - service.name          # Critical: identifies the service
    - service.namespace     # Logical grouping
    - deployment.environment  # prod/staging/dev
    - k8s.namespace.name    # Kubernetes namespace
    - k8s.pod.name          # Pod-level cardinality &amp;mdash; consider omitting in high-scale
    - k8s.node.name         # Useful for infrastructure correlation&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Avoid blindly promoting &lt;code&gt;k8s.pod.name&lt;/code&gt; at scale &amp;mdash; in a cluster with thousands of short-lived pods, this creates significant cardinality pressure. Prefer &lt;code&gt;service.name&lt;/code&gt; and &lt;code&gt;service.namespace&lt;/code&gt; for most alerting use cases, reserving pod-level labels for debugging dashboards.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;UTF-8 Metric Names: Why This Is the Real Game-Changer&lt;/h2&gt;



&lt;p&gt;To appreciate why UTF-8 metric name support matters so much, you need to understand the friction it eliminates. OpenTelemetry semantic conventions define metric names using dots as namespace separators. The canonical HTTP server duration metric is &lt;code&gt;http.server.request.duration&lt;/code&gt;. The canonical database query duration is &lt;code&gt;db.client.operation.duration&lt;/code&gt;. These names are standardized across languages and frameworks &amp;mdash; your Go service and your Java service and your Python service all emit the same metric name when instrumented with OTel.&lt;/p&gt;



&lt;p&gt;Prometheus 2.x could not store these names. The dots are illegal characters in Prometheus metric naming. Every OTel-to-Prometheus bridge &amp;mdash; the OpenTelemetry Collector&amp;rsquo;s Prometheus exporter, prom-client compatibility layers, the older &lt;code&gt;prometheusremotewrite&lt;/code&gt; exporter &amp;mdash; had to translate these names, typically by replacing dots with underscores: &lt;code&gt;http_server_request_duration&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;This translation is lossy and creates multiple problems:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;&lt;strong&gt;Name collisions&lt;/strong&gt;: &lt;code&gt;http.server.request_duration&lt;/code&gt; and &lt;code&gt;http.server.request.duration&lt;/code&gt; both become &lt;code&gt;http_server_request_duration&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Dashboard breakage&lt;/strong&gt;: Grafana dashboards built against OTel semantic conventions don&amp;rsquo;t work against translated Prometheus metrics without modification&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Cross-signal correlation&lt;/strong&gt;: Trace attributes use dot notation; when metric names differ, automated correlation tools lose the thread&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Vendor lock-in pressure&lt;/strong&gt;: Teams end up with separate naming conventions for &amp;ldquo;Prometheus metrics&amp;rdquo; vs &amp;ldquo;OTel metrics&amp;rdquo; and maintain both&lt;/li&gt;&lt;/ul&gt;



&lt;p&gt;Prometheus 3.0 with UTF-8 support stores &lt;code&gt;http.server.request.duration&lt;/code&gt; natively. No translation. No collision. The metric name you instrument with is the metric name you query.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Enabling UTF-8 Metric Names&lt;/h3&gt;



&lt;p&gt;UTF-8 metric names require the &lt;code&gt;utf8-names&lt;/code&gt; feature flag:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --enable-feature=utf8-names \
  --enable-feature=otlp-write-receiver&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Once enabled, PromQL queries must use quoted metric names when the name contains characters outside the legacy character set:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# Legacy metric name &amp;mdash; unquoted works fine
http_server_requests_total

# OTel metric name with dots &amp;mdash; requires quoting in PromQL
{&quot;__name__&quot;=&quot;http.server.request.duration&quot;}

# Or using the new PromQL syntax in Prometheus 3.0
http.server.request.duration{service_name=&quot;api-gateway&quot;}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The PromQL parser in Prometheus 3.0 has been updated to handle quoted metric names as a first-class construct. Grafana&amp;rsquo;s PromQL engine has also been updated to handle this syntax &amp;mdash; verify your Grafana version (10.3+ has full support) before deploying.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;OTel SDK to Prometheus 3.0 Directly: No Collector Required&lt;/h2&gt;



&lt;p&gt;For teams that only need to get application metrics into Prometheus, native OTLP ingestion enables a dramatically simpler architecture. Here&amp;rsquo;s what it looks like with different OTel SDKs.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Go (OpenTelemetry SDK)&lt;/h3&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;package main

import (
    &quot;context&quot;
    &quot;time&quot;

    &quot;go.opentelemetry.io/otel&quot;
    &quot;go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp&quot;
    &quot;go.opentelemetry.io/otel/sdk/metric&quot;
    &quot;go.opentelemetry.io/otel/sdk/resource&quot;
    semconv &quot;go.opentelemetry.io/otel/semconv/v1.26.0&quot;
)

func initMetrics(ctx context.Context) (*metric.MeterProvider, error) {
    res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceName(&quot;my-api&quot;),
            semconv.ServiceNamespace(&quot;platform&quot;),
            semconv.DeploymentEnvironment(&quot;production&quot;),
        ),
    )
    if err != nil {
        return nil, err
    }

    // Point directly at Prometheus 3.0 OTLP endpoint
    exporter, err := otlpmetrichttp.New(ctx,
        otlpmetrichttp.WithEndpoint(&quot;prometheus:9090&quot;),
        otlpmetrichttp.WithURLPath(&quot;/api/v1/otlp/v1/metrics&quot;),
        otlpmetrichttp.WithInsecure(), // Use WithTLSClientConfig for production
    )
    if err != nil {
        return nil, err
    }

    provider := metric.NewMeterProvider(
        metric.WithResource(res),
        metric.WithReader(
            metric.NewPeriodicReader(exporter,
                metric.WithInterval(30*time.Second),
            ),
        ),
    )

    otel.SetMeterProvider(provider)
    return provider, nil
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Python (OpenTelemetry SDK)&lt;/h3&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource, SERVICE_NAME, SERVICE_NAMESPACE

resource = Resource.create({
    SERVICE_NAME: &quot;my-api&quot;,
    SERVICE_NAMESPACE: &quot;platform&quot;,
    &quot;deployment.environment&quot;: &quot;production&quot;,
})

exporter = OTLPMetricExporter(
    endpoint=&quot;http://prometheus:9090/api/v1/otlp/v1/metrics&quot;,
)

reader = PeriodicExportingMetricReader(
    exporter,
    export_interval_millis=30_000,
)

provider = MeterProvider(resource=resource, metric_readers=[reader])
metrics.set_meter_provider(provider)

# Use the meter
meter = metrics.get_meter(&quot;my-api&quot;)
request_counter = meter.create_counter(
    name=&quot;http.server.request.count&quot;,
    description=&quot;Total HTTP server requests&quot;,
    unit=&quot;1&quot;,
)
request_duration = meter.create_histogram(
    name=&quot;http.server.request.duration&quot;,
    description=&quot;HTTP server request duration&quot;,
    unit=&quot;s&quot;,
)&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Java (OpenTelemetry SDK with Spring Boot)&lt;/h3&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# application.properties (Spring Boot with OTel auto-instrumentation)
otel.service.name=my-api
otel.resource.attributes=service.namespace=platform,deployment.environment=production

# Configure OTLP exporter to push directly to Prometheus
otel.metrics.exporter=otlp
otel.exporter.otlp.metrics.endpoint=http://prometheus:9090/api/v1/otlp/v1/metrics
otel.exporter.otlp.metrics.protocol=http/protobuf

# Export interval
otel.metric.export.interval=30000&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;With Spring Boot and the OTel Java agent, no code changes are required beyond configuration &amp;mdash; the agent instruments your HTTP server, database clients, and messaging systems automatically and pushes metrics using the names defined in OTel semantic conventions.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;OTel Collector to Prometheus 3.0: When You Need the Intermediary&lt;/h2&gt;



&lt;p&gt;Native OTLP ingestion is compelling, but the OpenTelemetry Collector remains relevant for a significant set of use cases. Understanding when each pattern is appropriate is the core architectural decision you will face when adopting Prometheus 3.0 in an OTel environment.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Pattern 1: OTel Collector as Fan-Out Gateway&lt;/h3&gt;



&lt;p&gt;When you need to send metrics to multiple backends simultaneously &amp;mdash; Prometheus for alerting, a long-term store like Thanos for historical analysis, and a commercial observability platform for full-stack correlation &amp;mdash; the OTel Collector handles fan-out efficiently. Applications push once to the Collector; the Collector distributes to all backends.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 10s
    send_batch_size: 1000
  memory_limiter:
    check_interval: 1s
    limit_mib: 512

exporters:
  # Push to Prometheus 3.0 via OTLP
  otlphttp/prometheus:
    endpoint: http://prometheus:9090/api/v1/otlp
    tls:
      insecure: true

  # Fan-out to Thanos via remote_write
  prometheusremotewrite/thanos:
    endpoint: http://thanos-receive:10908/api/v1/receive
    resource_to_telemetry_conversion:
      enabled: true

  # Fan-out to commercial backend
  otlp/datadog:
    endpoint: &lt;a href=&quot;https://otel-intake.datadoghq.com&quot; rel=&quot;nofollow&quot;&gt;https://otel-intake.datadoghq.com&lt;/a&gt;
    headers:
      DD-API-KEY: &quot;${DD_API_KEY}&quot;

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/prometheus, prometheusremotewrite/thanos, otlp/datadog]&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Pattern 2: Collector for Metric Transformation&lt;/h3&gt;



&lt;p&gt;The OTel Collector&amp;rsquo;s transform processor and metricstransform processor allow you to reshape metrics before they reach Prometheus: rename labels, add static attributes, filter out high-cardinality series, aggregate metrics to reduce storage cost, or apply unit conversions. These operations are not available in Prometheus&amp;rsquo;s native OTLP receiver.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;processors:
  transform/metrics:
    metric_statements:
      - context: metric
        statements:
          # Drop internal debug metrics
          - delete_matching_keys(attributes, &quot;internal.*&quot;)
          # Normalize environment label values
          - set(attributes[&quot;deployment.environment&quot;], &quot;prod&quot;)
            where attributes[&quot;deployment.environment&quot;] == &quot;production&quot;

  filter/drop_debug:
    metrics:
      exclude:
        match_type: regexp
        metric_names:
          - &quot;.*\.debug\..*&quot;
          - &quot;runtime\.go\.internal\..*&quot;

  metricstransform:
    transforms:
      # Rename a metric to match your existing Prometheus naming convention
      - include: http.server.request.duration
        action: update
        new_name: http_server_request_duration_seconds&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Pattern 3: Collector for Traces and Logs (Always Required)&lt;/h3&gt;



&lt;p&gt;If your architecture includes traces and logs alongside metrics &amp;mdash; and in 2025 it almost certainly does &amp;mdash; you need an OTel Collector regardless of what you do with metrics. Prometheus does not accept traces or logs. Jaeger, Tempo, and Loki all have their own ingestion protocols. The Collector is the universal routing layer for the three pillars of observability.&lt;/p&gt;



&lt;p&gt;In this architecture, it is usually simpler to route all three signals through the Collector and let it push metrics to Prometheus via OTLP or remote_write, rather than splitting metrics to go directly and everything else through the Collector.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;When to Use Native OTLP vs. OTel Collector: Decision Framework&lt;/h2&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;/th&gt;&lt;th&gt;Native OTLP&lt;/th&gt;&lt;th&gt;OTel Collector&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Single metrics backend (Prometheus only)&lt;/td&gt;&lt;td&gt;Preferred&lt;/td&gt;&lt;td&gt;Overkill&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Multiple metrics backends&lt;/td&gt;&amp;lt;td&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/15636/prometheus-3-0-and-opentelemetry-native-otlp-support-explained</guid>
<pubDate>Mon, 27 Apr 2026 23:49:40 +0000</pubDate>
</item>
</channel>
</rss>