<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Purity Udeh - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Purity Udeh - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 26 Aug 2026 17:05:26 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/TheOnlyPurity/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Convert Prometheus Histograms to OTLP with the OpenTelemetry Collector ]]>
                </title>
                <description>
                    <![CDATA[ Modern applications often expose metrics at a /metrics endpoint using the Prometheus format. Among these metrics, histograms are particularly useful. They show how often values fall into different ran ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-convert-prometheus-histogramsotlp-with-the-opentelemetry-collector/</link>
                <guid isPermaLink="false">6a8c4969642222471a04f943</guid>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Open Source ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloud native ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #prometheus ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops articles ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Purity Udeh ]]>
                </dc:creator>
                <pubDate>Sat, 22 Aug 2026 03:00:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/3dccb28f-d024-4c7a-9ac7-353594572842.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Modern applications often expose metrics at a <code>/metrics</code> endpoint using the Prometheus format.</p>
<p>Among these metrics, histograms are particularly useful. They show how often values fall into different ranges, like HTTP request durations, database query times, or queue processing latencies.</p>
<p>Unlike simple averages, histograms show the full picture: you can see how many requests are fast, how many are slow, and where the occasional outliers occur that might be silently degrading the user experience.</p>
<p>In payment systems, for instance, a sudden spike in transactions can expose hidden bottlenecks. Most of the requests might complete quickly, but a small percentage of slow transactions can ripple through the system, impacting retries, failures, and overall throughput. Histograms help identify these issues early by showing how values are distributed and highlighting outliers that averages obscure.</p>
<p>But not all backends understand Prometheus metrics natively. Many modern observability platforms prefer <strong>OTLP (OpenTelemetry Protocol)</strong>. Forwarding Prometheus metrics without converting them can lead to incomplete or misinterpreted data. That’s why we need a pipline to scrape, transform, and export histograms into OTLP so that your observability pipeline remains consistent and actionable.</p>
<p>In this article, we'll use the OpenTelemetry Collector to scrape Prometheus histograms from application <code>/metrics</code> endpoints, map them to the OpenTelemetry Histogram data model, and export them to our observability backend using OTLP. The Collector acts as a bridge that preserves data fidelity while ensuring compatibility with your monitoring platform.</p>
<p>To make this concrete, we'll use a small FastAPI application that simulates payment transactions. It exposes two Prometheus metrics: <code>payment_transaction_duration_seconds</code> (a histogram tracking how long each transaction takes) and <code>payment_transactions_total</code> (a counter of completed transactions). You can follow along with your own instrumented application, as anything exposing Prometheus metrics at <code>/metrics</code> will work the same way. This is the metric we'll follow from the application all the way to the observability backend.</p>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-1-how-to-scrape-metrics-with-prometheus-receiver"><strong>1. How to Scrape Metrics with Prometheus Receiver</strong></a></p>
</li>
<li><p><a href="#heading-2-transforming-prometheus-histograms"><strong>2. Transforming Prometheus Histograms</strong></a></p>
</li>
<li><p><a href="#heading-3-exporting-metrics-via-otlp"><strong>3. Exporting Metrics via OTLP</strong></a></p>
</li>
<li><p><a href="#heading-4-putting-the-pipeline-together"><strong>4. Putting the Pipeline Together</strong></a></p>
</li>
<li><p><a href="#heading-5-running-the-opentelemetry-collector"><strong>5. Running the OpenTelemetry Collector</strong></a></p>
<ul>
<li><p><a href="#heading-51-set-up-signoz-cloud"><strong>5.1 Set Up SigNoz Cloud</strong></a></p>
</li>
<li><p><a href="#heading-52-start-the-fastapi-application"><strong>5.2 Start the FastAPI Application</strong></a></p>
</li>
<li><p><a href="#heading-53-start-the-collector"><strong>5.3 Start the Collector</strong></a></p>
</li>
<li><p><a href="#heading-54-generate-test-transactions"><strong>5.4 Generate Test Transactions</strong></a></p>
</li>
<li><p><a href="#heading-55-confirm-backend-receipt"><strong>5.5 Confirm Backend Receipt</strong></a></p>
</li>
<li><p><a href="#heading-56-troubleshooting"><strong>5.6 Troubleshooting</strong></a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion"><strong>Conclusion</strong></a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, make sure you have:</p>
<ul>
<li><p>Docker installed</p>
</li>
<li><p>An application exposing Prometheus metrics through a <code>/metrics</code> endpoint</p>
</li>
<li><p>An OTLP-compatible observability backend</p>
</li>
<li><p>Basic knowledge of Prometheus metrics</p>
</li>
<li><p>Basic knowledge of YAML</p>
</li>
<li><p>Basic familiarity with Docker and OpenTelemetry</p>
</li>
</ul>
<p>You don't need advanced OpenTelemetry knowledge to follow this tutorial. I'll walk through the Prometheus histogram and show what happens to it as it moves through the OpenTelemetry Collector.</p>
<h2 id="heading-1-how-to-scrape-metrics-with-prometheus-receiver">1. How to Scrape Metrics with Prometheus Receiver</h2>
<p>First, we'll collect metrics from the application. The demo application exposes its Prometheus metrics at the <code>/metrics</code> endpoint, and the Prometheus receiver periodically scrapes this endpoint and ingests the metrics into the observability pipeline. You can also inspect <code>/metrics</code> directly to see the data before the Collector reads it.</p>
<p>Configuration setup example:</p>
<pre><code class="language-yaml">receivers:
  prometheus:
    config:
      scrape_configs:
        - job_name: payment-demo
          scrape_interval: 15s
          static_configs:
            - targets: ["payment-api:8080"]
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/5e891138-0408-41ee-9ae9-a68ac056aa81.png" alt="FastAPI /metrics output showing the payment transaction duration histogram in Prometheus format" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>What's happening behind the scenes here:</p>
<p><code>scrape_interval</code> controls how often the Collector scrapes the target. Here, we're using 15 seconds; adjust it based on how frequently you need metric updates and the load your application can handle.</p>
<p>In high-throughput systems like payment platforms or real-time processing services, the scrape interval becomes particularly important. Setting it too long may cause you to miss short-lived performance issues or transient errors, while setting it too short risks overwhelming the service with scraping requests or generating excessive network traffic.</p>
<p><code>targets</code> lists the specific endpoints that expose Prometheus metrics. You can add multiple targets when you need to scrape more than one service.</p>
<p>Finally, <code>job_name</code> is an identifier that helps group metrics logically, making them easier to manage and analyze once they reach your backend.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770317251340/2853e8ee-b3d9-49c4-b931-3194eb2ea7d2.png" alt="Diagram showing an OpenTelemetry Collector discovering metrics targets, assigning targets, querying endpoints, and scraping metrics." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-2-transforming-prometheus-histograms">2. Transforming Prometheus Histograms</h2>
<p>Once the metrics are scraped, the next step is transformation. Prometheus exposes histograms as multiple time series:</p>
<ul>
<li><p><code>_bucket</code> shows how many requests fall below a certain duration.</p>
</li>
<li><p><code>_sum</code> is the total of all observed durations.</p>
</li>
<li><p><code>_count</code> is the number of observations.</p>
</li>
</ul>
<p>The <code>payment_transaction_duration_seconds</code> histogram records how long each payment transaction takes. Prometheus exposes it as <code>_bucket</code>, <code>_count</code>, and <code>_sum</code> series. When the Prometheus receiver collects these metrics, it converts them into the OpenTelemetry Histogram data model, which can then be exported through OTLP while preserving the information needed to analyze transaction latency and calculate percentiles.</p>
<p>Without histograms, averages obscure latency distributions. If most requests complete in 50ms but 5% take 2+ seconds, the average of 150ms masks that serious performance issue. Histograms capture the complete picture by recording how many observations fall into each latency bucket.</p>
<p>Keeping the distribution allows you to identify changes in latency and investigate issues such as slow database queries, overloaded services, or delays from downstream dependencies.</p>
<h3 id="heading-actual-prometheus-histogram">Actual Prometheus Histogram</h3>
<p>This is how your actual <code>/metrics</code> output would look, for example:</p>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/8d1e4857-7d39-4cb1-99f7-5ce184adb59c.png" alt="Prometheus /metrics output showing the payment transaction duration histogram as _bucket time series with different latency boundaries" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>And here's the actual OpenTelemetry Histogram representation:</p>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/ef856e3e-99d5-42af-9f39-3eb88d752505.png" alt="OpenTelemetry Collector output showing the payment_transaction_duration_seconds metric as a Histogram with bucket boundaries and counts." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The output above shows the same transaction-duration data represented as an OpenTelemetry Histogram. Instead of three separate Prometheus series, the Collector now has one histogram containing the count, sum, explicit bucket boundaries, and bucket counts.</p>
<h2 id="heading-3-exporting-metrics-via-otlp">3. Exporting Metrics via OTLP</h2>
<p>Once the metrics have been scraped and processed, the Collector sends them to your observability backend using OTLP. The OTLP exporter sends the processed metrics, including histograms, counters, and gauges, to the backend.</p>
<pre><code class="language-yaml">exporters:
&nbsp; otlp:
&nbsp; &nbsp; endpoint: "otlp.backend.example.com:4317"
&nbsp; &nbsp; tls:
&nbsp; &nbsp; &nbsp; insecure: false
</code></pre>
<p><code>endpoint</code> specifies the backend address for receiving OTLP metrics. This typically points to a central observability platform that aggregates metrics from multiple services across your infrastructure.</p>
<p><code>tls</code> ensures secure data transmission between the collector and your backend. Set <code>insecure: true</code> only when intentionally connecting to an endpoint that does not use TLS, such as some local development setups</p>
<h2 id="heading-4-putting-the-pipeline-together">4. Putting the Pipeline Together</h2>
<p>We've looked at each part of the pipeline individually. Now let's connect them and follow a metric from the application all the way to the backend.</p>
<pre><code class="language-yaml">service:
  pipelines:
    metrics:
      receivers: [prometheus]
      exporters: [otlp]
</code></pre>
<p>This is the complete path our <code>payment_transaction_duration_seconds</code> metric follows, from the FastAPI application to the observability backend. The diagram above shows this flow. The next section walks through actually running it and connecting to SigNoz</p>
<p><strong>Pipeline flow:</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/ba4d6362-0e7a-4194-ba89-6fb3b4687489.png" alt="Pipeline flow architecture" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The above architecture diagram shows a FastAPI application exposing metrics through /metrics, the OpenTelemetry Collector scraping them with the Prometheus receiver, processing the metrics, and exporting them through OTLP to an observability backend.</p>
<h2 id="heading-5-running-the-opentelemetry-collector">5. Running the OpenTelemetry Collector</h2>
<p>We'll run the complete pipeline and verify that the transaction metrics make it from the application to SigNoz. Follow these steps (which I'll walk you through in detail below):</p>
<ol>
<li><p><strong>Set up SigNoz Cloud:</strong> Configure the endpoint and ingestion key that the Collector will use.</p>
</li>
<li><p><strong>Start the FastAPI application:</strong> The application exposes the Prometheus metrics at <code>/metrics</code>.</p>
</li>
<li><p><strong>Start the OpenTelemetry Collector:</strong> The Collector begins scraping the application's <code>/metrics</code> endpoint using the Prometheus receiver.</p>
</li>
<li><p><strong>Generate test transactions:</strong> Send requests to the application to create transaction-duration measurements.</p>
</li>
<li><p><strong>Verify the Collector and backend:</strong> Check the Collector logs to confirm that the pipeline is running, then open SigNoz and verify that the <code>payment_transaction_duration_seconds</code> metric has arrived.</p>
</li>
<li><p>Troubleshooting</p>
</li>
</ol>
<h3 id="heading-51-set-up-signoz-cloud">5.1 Set Up SigNoz Cloud</h3>
<p>SigNoz provides the observability backend that will receive the metrics exported by the OpenTelemetry Collector. For this demo, we'll use SigNoz Cloud as the observability backend, so there's nothing to install locally.</p>
<p>First, sign up for a free account at <a href="http://signoz.io">signoz.io</a>. In the SigNoz Cloud dashboard, go to <strong>Settings → Ingestion Keys</strong>. The page shows your Ingestion URL, Region, and Ingestion Key.</p>
<p>Add these to a <code>.env</code> file in your project directory:</p>
<pre><code class="language-shell">SIGNOZ_INGESTION_KEY=your-real-key
SIGNOZ_OTLP_ENDPOINT=ingest.&lt;your-region&gt;.signoz.cloud:443
</code></pre>
<p>Treat the ingestion key like a password and never commit it or share it publicly.</p>
<p>Reference those variables in your Collector configuration:</p>
<pre><code class="language-shell">exporters:
  otlp:
    endpoint: "${SIGNOZ_OTLP_ENDPOINT}"
    tls:
      insecure: false
    headers:
      signoz-ingestion-key: "${SIGNOZ_INGESTION_KEY}"
</code></pre>
<p>Then add <code>.env</code> to your <code>.gitignore</code> so the key never gets committed:</p>
<pre><code class="language-plaintext">echo ".env" &gt;&gt; .gitignore
</code></pre>
<h3 id="heading-52-start-the-fastapi-application">5.2 Start the FastAPI Application.</h3>
<p>Start the application on port 8080:</p>
<pre><code class="language-shell">uvicorn app.main:app --reload --port 8080
</code></pre>
<p>Verify that the application is running:</p>
<pre><code class="language-shell">curl http://localhost:8080/
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/b27bcf94-e5da-43e6-b51f-f17fe06d90d9.png" alt="FastAPI payment demo running locally at 127.0.0.1:8080, displaying a JSON response confirming that the payment transaction demo is running" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now inspect the Prometheus metrics:</p>
<pre><code class="language-shell">curl -Ls http://localhost:8080/metrics
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/10c4e1d5-d117-419d-8557-f94884f0bad8.png" alt="Browser showing the FastAPI payment demo running successfully at localhost:8080" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The <code>/metrics</code> endpoint exposes the application's Prometheus metrics, including the <code>payment_transaction_duration_seconds</code> histogram that the Collector will scrape.</p>
<h3 id="heading-53-start-the-collector">5.3 Start the Collector</h3>
<p>With the application running and the Collector configuration in place, start the Collector:</p>
<pre><code class="language-shell">docker compose up --build
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/3ef283ca-bea7-4fd3-82b5-9684c3b5dea9.png" alt="Docker Compose logs showing the payment API running and the Collector successfully scraping its /metrics endpoint." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Check the Collector logs to confirm that the pipeline is running and that metrics are being processed.</p>
<p>This builds the <code>payment-api</code> image, starts both containers on the shared <code>telemetry</code> network, and the Collector immediately begins scraping <code>/metrics</code> from the application using the Prometheus receiver, reading your SigNoz ingestion key and endpoint from <code>.env</code> automatically.</p>
<h3 id="heading-54-generate-test-transactions">5.4 Generate Test Transactions</h3>
<p>Generate transactions with different processing times:</p>
<pre><code class="language-shell">curl -X POST "http://localhost:8080/transactions?delay_ms=50"

curl -X POST "http://localhost:8080/transactions?delay_ms=250"

curl -X POST "http://localhost:8080/transactions?delay_ms=2500"
</code></pre>
<p>Generate additional requests if you want more observations in the histogram.</p>
<p>These requests create transaction-duration observations that are recorded by the <code>payment_transaction_duration_seconds</code> histogram. The Collector picks up the updated metric during its next scrape.</p>
<h3 id="heading-55-confirm-backend-receipt">5.5 Confirm Backend Receipt</h3>
<p>Open SigNoz Cloud and search for:</p>
<pre><code class="language-plaintext">payment_transaction_duration_seconds
</code></pre>
<p>The metric should be represented as a <strong>histogram</strong>, with its bucket distribution <code>_bucket</code>, <code>_sum</code>, and <code>_count</code> available to the backend rather than appearing as unrelated Prometheus series.</p>
<img src="https://cdn.hashnode.com/uploads/covers/611e0999c4783a33f5e25171/e4ea5b60-b7d4-4435-a427-d738e45ad19b.png" alt="SigNoz Metrics Explorer showing payment_transaction_duration_seconds as a histogram with associated bucket, count, and sum data after being exported through the OpenTelemetry Collector." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-56-troubleshooting">5.6 Troubleshooting</h3>
<p>If metrics aren't flowing correctly, start by checking the log:</p>
<pre><code class="language-bash">docker logs &lt;collector-container&gt;
</code></pre>
<p>Look for connection errors, authentication failures, failed scrape attempts, or configuration errors.</p>
<p>Also verify that:</p>
<ul>
<li><p>The FastAPI application is running.</p>
</li>
<li><p><code>/metrics</code> is accessible.</p>
</li>
<li><p>The Collector can reach the application.</p>
</li>
<li><p>The SigNoz endpoint and ingestion key are correct.</p>
</li>
<li><p>The <code>.env</code> variables are available to the Collector.</p>
</li>
<li><p>The receiver and exporter names match the pipeline configuration.</p>
</li>
</ul>
<p>Use the <code>--dry-run</code> flag if available to validate before deployment.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Prometheus histograms provide a useful view of transaction latency by showing how observations are distributed across different buckets rather than reducing them to a single average.</p>
<p>In this tutorial, we followed <code>payment_transaction_duration_seconds</code> from a FastAPI application's <code>/metrics</code> endpoint through the OpenTelemetry Collector and into SigNoz.</p>
<p>The Prometheus receiver mapped the <code>_bucket</code>, <code>_count</code>, and <code>_sum</code> series into the OpenTelemetry Histogram data model, preserving the distribution of transaction durations for analysis in the backend.</p>
<p>This allows the same metric to move from a Prometheus-instrumented application into an OTLP-based observability platform without manually reconstructing the histogram.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
