<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Farhan Munir</title>
    <description>The latest articles on DEV Community by Farhan Munir (@munirfarhan).</description>
    <link>https://dev.to/munirfarhan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3833974%2Fd6f31945-fbf3-430a-aca8-9937f9230037.jpg</url>
      <title>DEV Community: Farhan Munir</title>
      <link>https://dev.to/munirfarhan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/munirfarhan"/>
    <language>en</language>
    <item>
      <title>Milestone 4 Complete — OTLP HTTP Exporter for Heka Insights Agent</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Wed, 29 Apr 2026 06:49:27 +0000</pubDate>
      <link>https://dev.to/munirfarhan/milestone-4-complete-otlp-http-exporter-for-heka-insights-agent-1chp</link>
      <guid>https://dev.to/munirfarhan/milestone-4-complete-otlp-http-exporter-for-heka-insights-agent-1chp</guid>
      <description>&lt;p&gt;In this milestone, I completed the OTLP HTTP exporter implementation for &lt;strong&gt;Heka Insights Agent&lt;/strong&gt; and validated it with both unit tests and Docker-backed integration tests.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;https://github.com/ronin1770/heka-insights-agent&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What was implemented in Milestone 4
&lt;/h2&gt;

&lt;p&gt;Milestone 4 includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OTLP HTTP exporter wiring&lt;/li&gt;
&lt;li&gt;OTLP metric payload mapping&lt;/li&gt;
&lt;li&gt;OTLP auth header support&lt;/li&gt;
&lt;li&gt;OTLP resource attribute mapping&lt;/li&gt;
&lt;li&gt;Retry/backoff behavior for transient failures&lt;/li&gt;
&lt;li&gt;Unit test coverage for config + sender + exporter + mapping&lt;/li&gt;
&lt;li&gt;Docker integration tests against a real OpenTelemetry Collector&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Configuration added
&lt;/h2&gt;

&lt;p&gt;OTLP configuration is environment-driven through &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOG_LOCATION=./log/heka_agent.log
CPU_POLL_INTERVAL_SECONDS=10
EXPORTER_TYPE=otlp_http
OTLP_HTTP_ENDPOINT=http://localhost:4318/v1/metrics
OTLP_HTTP_HEADERS=key=Bearer abcd1234
OTLP_RESOURCE_ATTRIBUTES=service.name=heka-insights-agent,host.name=localhost
OTLP_HTTP_TIMEOUT_SECONDS=10
OTLP_HTTP_RETRY_MAX_ATTEMPTS=5
OTLP_HTTP_RETRY_INITIAL_BACKOFF_SECONDS=1
OTLP_HTTP_RETRY_MAX_BACKOFF_SECONDS=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OTLP_HTTP_HEADERS&lt;/code&gt; format: &lt;code&gt;key=value,key2=value2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OTLP_RESOURCE_ATTRIBUTES&lt;/code&gt; format: &lt;code&gt;key=value,key2=value2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Retryable failures:

&lt;ul&gt;
&lt;li&gt;transport errors&lt;/li&gt;
&lt;li&gt;HTTP &lt;code&gt;408&lt;/code&gt;, &lt;code&gt;429&lt;/code&gt;, and &lt;code&gt;5xx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Non-retryable failures:

&lt;ul&gt;
&lt;li&gt;HTTP &lt;code&gt;400&lt;/code&gt;, &lt;code&gt;401&lt;/code&gt;, &lt;code&gt;403&lt;/code&gt;, &lt;code&gt;404&lt;/code&gt;, and similar client-side errors&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Unit tests
&lt;/h2&gt;

&lt;p&gt;Unit tests cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OTLP env parsing and validation&lt;/li&gt;
&lt;li&gt;OTLP payload mapping&lt;/li&gt;
&lt;li&gt;OTLP HTTP sender behavior&lt;/li&gt;
&lt;li&gt;Retry/backoff behavior&lt;/li&gt;
&lt;li&gt;Exporter initialization and wiring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run unit tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;src python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; tests &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unit test output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ran 27 tests in 0.008s

OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Integration tests (Docker + real collector)
&lt;/h2&gt;

&lt;p&gt;Integration tests validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth success (&lt;code&gt;200&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;auth reject path (&lt;code&gt;401&lt;/code&gt;) and no retry on non-retryable status&lt;/li&gt;
&lt;li&gt;no-auth collector path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run OTLP integration tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUN_OTLP_INTEGRATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;src python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest &lt;span class="nt"&gt;-v&lt;/span&gt; tests.test_otlp_http_integration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integration test output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_auth_rejected_without_retry_for_401 ... ok
test_auth_success_with_bearer_header ... ok
test_no_auth_collector_accepts_without_headers ... ok

----------------------------------------------------------------------
Ran 3 tests in 2.417s

OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Collector config used in tests
&lt;/h2&gt;

&lt;p&gt;The project includes collector fixtures under &lt;code&gt;tests/fixtures/otlp/&lt;/code&gt;, including auth-required and no-auth variants for scenario testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;Milestone 4 now has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;working OTLP HTTP exporter&lt;/li&gt;
&lt;li&gt;production-style config controls&lt;/li&gt;
&lt;li&gt;retry/backoff logic for transient failures&lt;/li&gt;
&lt;li&gt;unit + integration test coverage&lt;/li&gt;
&lt;li&gt;updated docs and run instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo again: &lt;a href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;https://github.com/ronin1770/heka-insights-agent&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>devops</category>
      <category>telemetry</category>
    </item>
    <item>
      <title>Milestone 4 (Part 1): Implementing OTLP HTTP Core in Heka Insights Agent (M4-1, M4-2)</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Mon, 27 Apr 2026 09:57:30 +0000</pubDate>
      <link>https://dev.to/munirfarhan/milestone-4-part-1-implementing-otlp-http-core-in-heka-insights-agent-m4-1-m4-2-2a79</link>
      <guid>https://dev.to/munirfarhan/milestone-4-part-1-implementing-otlp-http-core-in-heka-insights-agent-m4-1-m4-2-2a79</guid>
      <description>&lt;h1&gt;
  
  
  Milestone 4 (Part 1): Implementing OTLP HTTP Core in Heka Insights Agent (M4-1, M4-2)
&lt;/h1&gt;

&lt;p&gt;Heka Insights Agent already had a canonical metrics pipeline from Milestone 3.&lt;br&gt;&lt;br&gt;
In this part of Milestone 4, I implemented the OTLP HTTP core in two focused steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;M4-1&lt;/code&gt;: Canonical metrics -&amp;gt; OTLP payload mapping layer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;M4-2&lt;/code&gt;: OTLP HTTP request sender and exporter wiring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post covers only these two items. Auth headers, resource attributes, retry/compression controls are intentionally deferred to later M4 tasks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Split Matters
&lt;/h2&gt;

&lt;p&gt;By separating mapping from transport, we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stable internal metric model&lt;/li&gt;
&lt;li&gt;explicit OTLP payload construction&lt;/li&gt;
&lt;li&gt;transport logic that can evolve independently&lt;/li&gt;
&lt;li&gt;clean foundation for New Relic/Datadog-style OTLP integrations later&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What Was Implemented
&lt;/h2&gt;
&lt;h3&gt;
  
  
  M4-1: OTLP Payload Mapping Layer
&lt;/h3&gt;

&lt;p&gt;I added a dedicated mapper that converts canonical metric records into OTLP HTTP JSON payloads.&lt;/p&gt;

&lt;p&gt;Core behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validates required canonical fields before send&lt;/li&gt;
&lt;li&gt;supports explicit type mapping:&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gauge&lt;/code&gt; -&amp;gt; OTLP &lt;code&gt;gauge.dataPoints&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter&lt;/code&gt; -&amp;gt; OTLP &lt;code&gt;sum.dataPoints&lt;/code&gt; with cumulative temporality&lt;/li&gt;
&lt;li&gt;maps canonical labels to OTLP metric attributes&lt;/li&gt;
&lt;li&gt;maps &lt;code&gt;timestamp_unix_ms&lt;/code&gt; to OTLP &lt;code&gt;timeUnixNano&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;rejects malformed metrics early with explicit errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: malformed payloads are blocked before network transport.&lt;/p&gt;
&lt;h3&gt;
  
  
  M4-2: OTLP HTTP Sender + Exporter
&lt;/h3&gt;

&lt;p&gt;I added OTLP HTTP sender/exporter flow and wired it into exporter selection.&lt;/p&gt;

&lt;p&gt;Core behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EXPORTER_TYPE=otlp_http&lt;/code&gt; now creates OTLP exporter&lt;/li&gt;
&lt;li&gt;validates OTLP endpoint format (&lt;code&gt;http/https&lt;/code&gt; absolute URL) at startup&lt;/li&gt;
&lt;li&gt;fails fast when endpoint is missing/invalid&lt;/li&gt;
&lt;li&gt;sends JSON payload via HTTP &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;treats only &lt;code&gt;2xx&lt;/code&gt; responses as success&lt;/li&gt;
&lt;li&gt;raises explicit errors for HTTP failures and transport errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: working end-to-end OTLP HTTP delivery with fail-fast startup safety.&lt;/p&gt;
&lt;h2&gt;
  
  
  Local Test Setup with OpenTelemetry Collector (Docker)
&lt;/h2&gt;

&lt;p&gt;I used OTel Collector debug exporter to validate incoming metrics.&lt;/p&gt;
&lt;h3&gt;
  
  
  Collector config (&lt;code&gt;otel-collector-config.yaml&lt;/code&gt;)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;

&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Run collector
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 4318:4318 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/otel-collector-config.yaml:/etc/otelcol/config.yaml"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  otel/opentelemetry-collector:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/otelcol/config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Agent &lt;code&gt;.env&lt;/code&gt; for this test
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOG_LOCATION=./log/heka_agent.log
CPU_POLL_INTERVAL_SECONDS=10
EXPORTER_TYPE=otlp_http
OTLP_HTTP_ENDPOINT=http://localhost:4318/v1/metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Run agent
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python src/main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Verification Signals
&lt;/h2&gt;

&lt;p&gt;From runtime behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;agent starts with &lt;code&gt;exporter_type=otlp_http&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;collector logs periodic metric batches every ~10 seconds&lt;/li&gt;
&lt;li&gt;no exporter exceptions during dispatch&lt;/li&gt;
&lt;li&gt;first cycle has fewer points due to CPU warm-up, then normalizes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example collector signal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;resource metrics: 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;metrics: 24&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data points: 24&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Tests Added
&lt;/h2&gt;

&lt;p&gt;I added focused tests for M4-1/M4-2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;payload mapping correctness (gauge/counter, labels, timestamps)&lt;/li&gt;
&lt;li&gt;validation failures for malformed canonical metrics&lt;/li&gt;
&lt;li&gt;HTTP sender request behavior and error handling&lt;/li&gt;
&lt;li&gt;exporter wiring and missing-endpoint startup failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All tests pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;src python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; tests &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Is Intentionally Not Included Yet
&lt;/h2&gt;

&lt;p&gt;Deferred to later M4 items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth headers (&lt;code&gt;M4-3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;resource attribute mapping (&lt;code&gt;M4-4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;timeout/compression/retry controls (&lt;code&gt;M4-5&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;broader OTLP docs and expanded test matrix (&lt;code&gt;M4-6&lt;/code&gt;, &lt;code&gt;M4-7&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;M4-1 and M4-2 establish the OTLP core path: canonical metrics are now mapped deterministically and sent over HTTP with fail-fast validation.&lt;br&gt;&lt;br&gt;
This gives a production-friendly base to layer auth, resource metadata, and resiliency controls next.&lt;/p&gt;

&lt;p&gt;Repo URL: &lt;a href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;https://github.com/ronin1770/heka-insights-agent&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>telemetry</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Heka-Insights-Agent: Milestone M3-4: Fail-Fast Exporter Validation at Startup</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Fri, 24 Apr 2026 06:30:06 +0000</pubDate>
      <link>https://dev.to/munirfarhan/heka-insights-agent-milestone-m3-4-fail-fast-exporter-validation-at-startup-14p</link>
      <guid>https://dev.to/munirfarhan/heka-insights-agent-milestone-m3-4-fail-fast-exporter-validation-at-startup-14p</guid>
      <description>&lt;h1&gt;
  
  
  Milestone M3-4: Fail-Fast Exporter Validation at Startup
&lt;/h1&gt;

&lt;p&gt;On &lt;strong&gt;April 24, 2026&lt;/strong&gt;, I completed &lt;strong&gt;M3-4&lt;/strong&gt; in &lt;code&gt;heka-insights-agent&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;M3-4: Add configuration validation for exporter settings&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This milestone closed a critical gap in the exporter foundation by removing silent fallback behavior and enforcing explicit startup failures for invalid exporter configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;In Milestone M3-3, we routed output through the exporter interface and lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;initialize()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;export(metrics)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shutdown()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That established the architecture.&lt;br&gt;&lt;br&gt;
M3-4 focused on correctness and operational safety: startup must fail early when exporter configuration is invalid or points to an unimplemented adapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Before M3-4
&lt;/h2&gt;

&lt;p&gt;Prior behavior was permissive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid &lt;code&gt;EXPORTER_TYPE&lt;/code&gt; values defaulted back to &lt;code&gt;console&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;unimplemented exporter types also downgraded to &lt;code&gt;console&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why this was risky:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;misconfiguration could go unnoticed in production&lt;/li&gt;
&lt;li&gt;users could think they were exporting to one backend while actually exporting to console&lt;/li&gt;
&lt;li&gt;behavior violated milestone acceptance criteria requiring explicit errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  M3-4 Goals
&lt;/h2&gt;

&lt;p&gt;The implementation targeted three concrete outcomes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;invalid &lt;code&gt;EXPORTER_TYPE&lt;/code&gt; must fail fast with a clear startup error&lt;/li&gt;
&lt;li&gt;configured but unimplemented exporter adapters must fail fast with a clear startup error&lt;/li&gt;
&lt;li&gt;docs must reflect strict validation (remove pre-M3-4 fallback messaging)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation Summary
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1) Strict validation in runtime config
&lt;/h2&gt;

&lt;p&gt;Updated &lt;code&gt;src/config/runtime.py&lt;/code&gt; in &lt;code&gt;get_exporter_type(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Behavior now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing &lt;code&gt;EXPORTER_TYPE&lt;/code&gt; still defaults to &lt;code&gt;console&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;unsupported value now raises &lt;code&gt;RuntimeError&lt;/code&gt; with explicit supported values list&lt;/li&gt;
&lt;li&gt;optional logger records an error message before raising&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This changed exporter selection from “best-effort fallback” to deterministic validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Removed fallback from exporter factory
&lt;/h2&gt;

&lt;p&gt;Updated &lt;code&gt;src/exporters/factory.py&lt;/code&gt; in &lt;code&gt;create_exporter(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Behavior now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;console&lt;/code&gt; returns &lt;code&gt;ConsoleExporter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;other configured values currently raise &lt;code&gt;RuntimeError&lt;/code&gt; because adapters are not implemented yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is intentional. It prevents false confidence and makes readiness of each exporter explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Documentation updated to M3-4 semantics
&lt;/h2&gt;

&lt;p&gt;Updated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docs/configuration.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both now state that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unsupported exporter values fail fast&lt;/li&gt;
&lt;li&gt;unimplemented exporters fail fast&lt;/li&gt;
&lt;li&gt;only missing value defaults to &lt;code&gt;console&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removed the “fallback to console with warning” wording from the pre-M3-4 behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation Performed
&lt;/h2&gt;

&lt;p&gt;Validation included compile checks and runtime behavior checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compile validation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;python3 -m compileall src&lt;/code&gt; passed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Behavior validation
&lt;/h2&gt;

&lt;p&gt;Tested startup resolution paths for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing &lt;code&gt;EXPORTER_TYPE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXPORTER_TYPE=console&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXPORTER_TYPE=invalid_value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPORTER_TYPE=otlp_http&lt;/code&gt; (declared but not implemented adapter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observed results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing value -&amp;gt; resolves to &lt;code&gt;console&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console&lt;/code&gt; -&amp;gt; exporter creation succeeds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;invalid_value&lt;/code&gt; -&amp;gt; immediate &lt;code&gt;RuntimeError&lt;/code&gt; with supported-values message&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;otlp_http&lt;/code&gt; -&amp;gt; immediate &lt;code&gt;RuntimeError&lt;/code&gt; stating exporter not implemented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These outcomes align with M3-4 requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this change matters operationally
&lt;/h2&gt;

&lt;p&gt;Fail-fast startup validation improves reliability in real deployments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;misconfigurations are caught immediately&lt;/li&gt;
&lt;li&gt;no hidden routing to fallback output&lt;/li&gt;
&lt;li&gt;deployment behavior is explicit and auditable&lt;/li&gt;
&lt;li&gt;future exporter rollout can be gated by implementation readiness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially important when teams automate deployments and rely on env-based configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural Impact
&lt;/h2&gt;

&lt;p&gt;After M3-4, exporter behavior is now strictly layered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;config validates selector&lt;/li&gt;
&lt;li&gt;factory enforces implementation availability&lt;/li&gt;
&lt;li&gt;runtime starts only on valid+implemented exporter path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives us a clean foundation for adding real transports (&lt;code&gt;otlp_http&lt;/code&gt;, &lt;code&gt;datadog_native&lt;/code&gt;, &lt;code&gt;newrelic_otlp&lt;/code&gt;) without changing collector logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;The natural next step after M3-4 is M3-5:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;document exporter lifecycle and responsibilities in architecture docs&lt;/li&gt;
&lt;li&gt;include startup validation expectations as part of operational guidance&lt;/li&gt;
&lt;li&gt;capture adapter implementation contract for future backend integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;M3-4 makes sure the system fails loudly when exporter config is wrong, which is exactly what a transport foundation should do before adding real outbound integrations.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>python</category>
      <category>telemetry</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Milestone M3-3: Refactoring Console Output Into an Exporter Pathway</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Fri, 24 Apr 2026 05:49:23 +0000</pubDate>
      <link>https://dev.to/munirfarhan/milestone-m3-3-refactoring-console-output-into-an-exporter-pathway-3c1k</link>
      <guid>https://dev.to/munirfarhan/milestone-m3-3-refactoring-console-output-into-an-exporter-pathway-3c1k</guid>
      <description>&lt;h1&gt;
  
  
  Milestone M3-3: Refactoring Console Output Into an Exporter Pathway
&lt;/h1&gt;

&lt;p&gt;On &lt;strong&gt;April 24, 2026&lt;/strong&gt;, I completed &lt;strong&gt;M3-3&lt;/strong&gt; for &lt;code&gt;heka-insights-agent&lt;/code&gt;: moving console output from direct printing in the main loop to a proper exporter lifecycle.&lt;/p&gt;

&lt;p&gt;This was part of Milestone #3 (&lt;code&gt;Transport And Exporter Foundation&lt;/code&gt;) and specifically targeted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;M3-3: Refactor console output to use exporter interface&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a cleaner delivery architecture where collectors remain untouched and output transport is now pluggable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why M3-3 mattered
&lt;/h2&gt;

&lt;p&gt;Before this change, the runtime loop handled collection, formatting, and printing in one place. That worked for a console-only stage, but it tightly coupled runtime behavior to one output path.&lt;/p&gt;

&lt;p&gt;For Milestone #3, the architecture needs to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a canonical metric model&lt;/li&gt;
&lt;li&gt;reusable formatters&lt;/li&gt;
&lt;li&gt;exporter lifecycle boundaries&lt;/li&gt;
&lt;li&gt;future transport adapters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;M3-3 was the bridge from “it prints metrics” to “it exports metrics through a contract.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What the code looked like before
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/main.py&lt;/code&gt; previously:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;collected CPU/memory/disk payloads&lt;/li&gt;
&lt;li&gt;formatted them with &lt;code&gt;PrometheusFormatter.format(...)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;called &lt;code&gt;print(prometheus_output, end="", flush=True)&lt;/code&gt; directly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That meant no exporter-owned lifecycle (&lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;shutdown&lt;/code&gt;), and no central place to swap output behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design goals for this refactor
&lt;/h2&gt;

&lt;p&gt;The implementation targeted four practical goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep collectors unchanged.&lt;/li&gt;
&lt;li&gt;Remove direct console printing from &lt;code&gt;main.py&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Route output through &lt;code&gt;Exporter.initialize()&lt;/code&gt;, &lt;code&gt;Exporter.export(...)&lt;/code&gt;, and &lt;code&gt;Exporter.shutdown()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Preserve current Prometheus console output shape for backward compatibility.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What was implemented
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1) Console exporter implementation
&lt;/h2&gt;

&lt;p&gt;Created &lt;code&gt;src/exporters/console.py&lt;/code&gt; with &lt;code&gt;ConsoleExporter(Exporter)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initialize()&lt;/code&gt;: prepare exporter state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;export(metrics)&lt;/code&gt;: format and emit canonical metrics to stdout&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutdown()&lt;/code&gt;: close lifecycle cleanly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ConsoleExporter&lt;/code&gt; now owns console writes. &lt;code&gt;main.py&lt;/code&gt; no longer writes metrics directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Exporter factory wiring
&lt;/h2&gt;

&lt;p&gt;Created &lt;code&gt;src/exporters/factory.py&lt;/code&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create_exporter(exporter_type, logger=...) -&amp;gt; Exporter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Current behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;returns &lt;code&gt;ConsoleExporter&lt;/code&gt; for &lt;code&gt;console&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;falls back to &lt;code&gt;ConsoleExporter&lt;/code&gt; for unimplemented exporter types with a warning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps runtime deterministic while other exporters are still pending.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Canonical metric normalization pipeline
&lt;/h2&gt;

&lt;p&gt;Created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/pipeline/canonical_metrics.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/pipeline/__init__.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Added &lt;code&gt;build_canonical_metrics(payloads, timestamp_unix_ms=...)&lt;/code&gt; to map collector payloads into canonical records.&lt;/p&gt;

&lt;p&gt;Canonical record fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;labels&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;optional &lt;code&gt;timestamp_unix_ms&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separated normalization from transport and moved us closer to the milestone’s delivery pipeline model.&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Formatter support for canonical metrics
&lt;/h2&gt;

&lt;p&gt;Extended &lt;code&gt;src/formatters/prometheus.py&lt;/code&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;format_canonical(metrics)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lets formatters operate on canonical data (not collector-specific payload dictionaries) while preserving the existing Prometheus text exposition output.&lt;/p&gt;

&lt;h2&gt;
  
  
  5) Main loop refactor to exporter lifecycle
&lt;/h2&gt;

&lt;p&gt;Updated &lt;code&gt;src/main.py&lt;/code&gt; so startup and runtime flow is now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;resolve &lt;code&gt;EXPORTER_TYPE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_exporter(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exporter.initialize()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;collect payloads each cycle&lt;/li&gt;
&lt;li&gt;normalize to canonical metrics&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exporter.export(canonical_metrics)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exporter.shutdown()&lt;/code&gt; in &lt;code&gt;finally&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Direct &lt;code&gt;print(...)&lt;/code&gt; of telemetry output was removed from &lt;code&gt;main.py&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  6) Python 3.10 typing compatibility fix
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/exporters/base.py&lt;/code&gt; originally used &lt;code&gt;typing.NotRequired&lt;/code&gt;, which is not available in Python 3.10’s &lt;code&gt;typing&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CanonicalMetric&lt;/code&gt; TypedDict was adjusted to a Python-3.10-safe form using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;required base TypedDict&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;total=False&lt;/code&gt; extension for optional timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This kept type intent intact without requiring runtime upgrades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation and runtime output
&lt;/h2&gt;

&lt;p&gt;Validation performed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;python3 -m compileall src&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;smoke execution path for exporter creation + canonical conversion + console export&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observed runtime behavior after refactor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collector logs still emitted&lt;/li&gt;
&lt;li&gt;Prometheus lines still emitted each cycle&lt;/li&gt;
&lt;li&gt;timestamps populated in Unix milliseconds&lt;/li&gt;
&lt;li&gt;disk metrics emitted as aggregate and per-device series&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: behavior stayed stable, but ownership moved to exporter architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  What M3-3 achieved (and what it didn’t)
&lt;/h2&gt;

&lt;p&gt;Completed in M3-3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;console output now runs through exporter interface&lt;/li&gt;
&lt;li&gt;runtime flow is wired to exporter lifecycle&lt;/li&gt;
&lt;li&gt;normalization and formatting layers are separated from collection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not part of M3-3 (handled in later items):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strict fail-fast invalid exporter value handling (&lt;code&gt;M3-4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;additional backend exporters (Datadog, OTLP HTTP, New Relic)&lt;/li&gt;
&lt;li&gt;retry/buffering semantics beyond base hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key lesson
&lt;/h2&gt;

&lt;p&gt;This milestone was less about adding features and more about installing architectural seams.&lt;/p&gt;

&lt;p&gt;The code now has explicit boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collectors collect&lt;/li&gt;
&lt;li&gt;pipeline normalizes&lt;/li&gt;
&lt;li&gt;formatter renders&lt;/li&gt;
&lt;li&gt;exporter delivers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That separation is what will let future backend integrations land without rewriting collectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up
&lt;/h2&gt;

&lt;p&gt;The immediate next step is &lt;code&gt;M3-4&lt;/code&gt;: enforce strict startup validation for unsupported &lt;code&gt;EXPORTER_TYPE&lt;/code&gt; values so runtime fails fast with explicit errors instead of warning + fallback.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>telemetry</category>
      <category>devops</category>
    </item>
    <item>
      <title>Next Milestones for Heka Insights Agent: From Console Output to Real Telemetry Delivery</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Tue, 21 Apr 2026 08:48:33 +0000</pubDate>
      <link>https://dev.to/munirfarhan/next-milestones-for-heka-insights-agent-from-console-output-to-real-telemetry-delivery-jep</link>
      <guid>https://dev.to/munirfarhan/next-milestones-for-heka-insights-agent-from-console-output-to-real-telemetry-delivery-jep</guid>
      <description>&lt;p&gt;We’re moving beyond console-only output and into real telemetry delivery.&lt;/p&gt;

&lt;p&gt;Over the next few milestones, &lt;strong&gt;heka-insights-agent&lt;/strong&gt; will introduce a proper exporter layer, OTLP HTTP support, and first-class integrations for &lt;strong&gt;New Relic&lt;/strong&gt; and &lt;strong&gt;Datadog&lt;/strong&gt;. The focus is to keep the agent vendor-agnostic at its core while making it easier to route system metrics into modern observability platforms using clean, configurable dispatch patterns.&lt;/p&gt;

&lt;p&gt;Project board: &lt;a href="https://github.com/users/ronin1770/projects/3/views/1" rel="noopener noreferrer"&gt;https://github.com/users/ronin1770/projects/3/views/1&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  python #observability #opentelemetry #devops #monitoring #prometheus #datadog #newrelic #opensource
&lt;/h1&gt;

</description>
      <category>devops</category>
      <category>python</category>
      <category>datadog</category>
      <category>telemetry</category>
    </item>
    <item>
      <title>✅ Milestone Completed: Prometheus Data Format Integration</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Sun, 19 Apr 2026 07:36:58 +0000</pubDate>
      <link>https://dev.to/munirfarhan/milestone-completed-prometheus-data-format-integration-12l8</link>
      <guid>https://dev.to/munirfarhan/milestone-completed-prometheus-data-format-integration-12l8</guid>
      <description>&lt;h1&gt;
  
  
  ✅ Milestone Completed: Prometheus Data Format Integration
&lt;/h1&gt;

&lt;p&gt;This sprint focused on making telemetry output fully Prometheus-compatible while preserving our existing collector pipeline for CPU, memory, and disk I/O.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I implemented
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Added Prometheus text exposition format (v0.0.4) output&lt;/li&gt;
&lt;li&gt;Included &lt;code&gt;# HELP&lt;/code&gt; and &lt;code&gt;# TYPE&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;Supported &lt;code&gt;gauge&lt;/code&gt; and &lt;code&gt;counter&lt;/code&gt; metric families&lt;/li&gt;
&lt;li&gt;Added label-based dimensions (CPU modes, disk devices)&lt;/li&gt;
&lt;li&gt;Kept output deterministic and scrape-ready for a &lt;code&gt;/metrics&lt;/code&gt; style endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Metrics now covered
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CPU usage and per-mode CPU time distribution&lt;/li&gt;
&lt;li&gt;Virtual memory: used, available, total&lt;/li&gt;
&lt;li&gt;Swap memory: used, total&lt;/li&gt;
&lt;li&gt;Disk I/O bytes: read/write (aggregate + per-device)&lt;/li&gt;
&lt;li&gt;Disk I/O operations: read/write (aggregate + per-device)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Validation results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Output verified against Prometheus exposition rules&lt;/li&gt;
&lt;li&gt;No syntax violations found&lt;/li&gt;
&lt;li&gt;Metrics are parseable and scrape-ready&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prior milestone context (OpenMetrics)
&lt;/h2&gt;

&lt;p&gt;Before this, I shipped OpenMetrics-aligned output for the same core Linux host metrics with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# HELP&lt;/code&gt;, &lt;code&gt;# TYPE&lt;/code&gt;, &lt;code&gt;# UNIT&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;# EOF&lt;/code&gt; termination&lt;/li&gt;
&lt;li&gt;Valid CPU, memory, and disk payload output&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate ratio-based CPU values (0–1) vs percentage&lt;/li&gt;
&lt;li&gt;Extend optional unit handling for stronger tooling interoperability&lt;/li&gt;
&lt;li&gt;Continue OpenMetrics alignment where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;https://github.com/ronin1770/heka-insights-agent&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>telemetry</category>
      <category>openmetrics</category>
      <category>prometheus</category>
    </item>
    <item>
      <title>Milestone 2: Standardizing Telemetry Output with JSON, Prometheus, and OpenMetrics</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Thu, 16 Apr 2026 15:25:23 +0000</pubDate>
      <link>https://dev.to/munirfarhan/milestone-2-standardizing-telemetry-output-with-json-prometheus-and-openmetrics-22ec</link>
      <guid>https://dev.to/munirfarhan/milestone-2-standardizing-telemetry-output-with-json-prometheus-and-openmetrics-22ec</guid>
      <description>&lt;h1&gt;
  
  
  Milestone 2: Standardizing Telemetry Output with JSON, Prometheus, and OpenMetrics
&lt;/h1&gt;

&lt;p&gt;In this milestone, we are focusing on one thing only: &lt;strong&gt;data format standardization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Heka Insights Agent already collects CPU, memory, and disk telemetry.&lt;br&gt;&lt;br&gt;
Now the goal is to emit the same logical metrics in three standard output formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;Prometheus text exposition&lt;/li&gt;
&lt;li&gt;OpenMetrics text format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Milestone Matters
&lt;/h2&gt;

&lt;p&gt;If an agent has no clear format strategy, every downstream integration becomes custom work.&lt;br&gt;&lt;br&gt;
That slows down adoption and increases maintenance cost.&lt;/p&gt;

&lt;p&gt;By standardizing format early, we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stable contracts for integrations&lt;/li&gt;
&lt;li&gt;easier validation and testing&lt;/li&gt;
&lt;li&gt;portability across observability stacks&lt;/li&gt;
&lt;li&gt;clearer boundaries between collection and export&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Milestone Scope (Only Data Format)
&lt;/h2&gt;

&lt;p&gt;This milestone does not include transports, retry logic, or backend adapters.&lt;br&gt;&lt;br&gt;
It only covers how telemetry is represented and serialized.&lt;/p&gt;

&lt;p&gt;Included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;canonical internal metric model&lt;/li&gt;
&lt;li&gt;naming/type/unit rules&lt;/li&gt;
&lt;li&gt;serializers for &lt;code&gt;json&lt;/code&gt;, &lt;code&gt;prometheus&lt;/code&gt;, &lt;code&gt;openmetrics&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;deterministic output behavior&lt;/li&gt;
&lt;li&gt;contract tests with golden files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Datadog/New Relic senders&lt;/li&gt;
&lt;li&gt;batching/compression/persistence&lt;/li&gt;
&lt;li&gt;new collector domains&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Canonical Metric Contract
&lt;/h2&gt;

&lt;p&gt;Every metric will be representable through one shared contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; (string)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; (string)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt; (&lt;code&gt;gauge&lt;/code&gt; or &lt;code&gt;counter&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unit&lt;/code&gt; (e.g. &lt;code&gt;bytes&lt;/code&gt;, &lt;code&gt;seconds&lt;/code&gt;, &lt;code&gt;percent&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; (number)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;labels&lt;/code&gt; (map of string to string; empty allowed)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;timestamp_unix_ms&lt;/code&gt; (optional integer)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This contract is the core design decision in Milestone 2.&lt;br&gt;&lt;br&gt;
Serializers consume this model and render format-specific output without changing metric meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming and Semantics Rules
&lt;/h2&gt;

&lt;p&gt;To keep the output stable and machine-friendly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;metric names are lowercase snake_case&lt;/li&gt;
&lt;li&gt;all names are prefixed with &lt;code&gt;heka_&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;counters end in &lt;code&gt;_total&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;unit suffixes are explicit (&lt;code&gt;_bytes&lt;/code&gt;, &lt;code&gt;_seconds&lt;/code&gt;, &lt;code&gt;_percent&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;label keys are lowercase snake_case&lt;/li&gt;
&lt;li&gt;metric identity must stay consistent across formats&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current Metric Mapping
&lt;/h2&gt;

&lt;p&gt;Initial canonical mapping includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;heka_cpu_usage_percent&lt;/code&gt; (gauge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_cpu_time_percent&lt;/code&gt; (gauge with &lt;code&gt;mode=&amp;lt;field&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_memory_virtual_used_bytes&lt;/code&gt; (gauge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_memory_virtual_available_bytes&lt;/code&gt; (gauge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_memory_virtual_total_bytes&lt;/code&gt; (gauge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_memory_swap_used_bytes&lt;/code&gt; (gauge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_memory_swap_total_bytes&lt;/code&gt; (gauge)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_disk_read_bytes_total&lt;/code&gt; (counter)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_disk_write_bytes_total&lt;/code&gt; (counter)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_disk_reads_total&lt;/code&gt; (counter)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;heka_disk_writes_total&lt;/code&gt; (counter)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Format-Specific Requirements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  JSON
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;UTF-8 JSON object&lt;/li&gt;
&lt;li&gt;includes &lt;code&gt;schema_version&lt;/code&gt; (starting at &lt;code&gt;v1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;includes &lt;code&gt;generated_at&lt;/code&gt; (RFC3339 UTC)&lt;/li&gt;
&lt;li&gt;includes top-level &lt;code&gt;metrics&lt;/code&gt; array&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prometheus
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prometheus text exposition format (&lt;code&gt;0.0.4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;include &lt;code&gt;# HELP&lt;/code&gt; and &lt;code&gt;# TYPE&lt;/code&gt; lines&lt;/li&gt;
&lt;li&gt;deterministic label ordering&lt;/li&gt;
&lt;li&gt;no OpenMetrics-only directives&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  OpenMetrics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OpenMetrics text format&lt;/li&gt;
&lt;li&gt;include &lt;code&gt;# HELP&lt;/code&gt;, &lt;code&gt;# TYPE&lt;/code&gt;, and &lt;code&gt;# UNIT&lt;/code&gt; when known&lt;/li&gt;
&lt;li&gt;terminate payload with &lt;code&gt;# EOF&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;metric names and labels remain aligned with Prometheus mode&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuration Contract
&lt;/h2&gt;

&lt;p&gt;One selector controls serialization:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OUTPUT_FORMAT=json|prometheus|openmetrics&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;default: &lt;code&gt;json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;invalid values: fail fast with a clear startup error&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acceptance Criteria
&lt;/h2&gt;

&lt;p&gt;Milestone 2 is done when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;same logical metric set is emitted in all three formats&lt;/li&gt;
&lt;li&gt;names/types/units are consistent&lt;/li&gt;
&lt;li&gt;Prometheus and OpenMetrics outputs validate&lt;/li&gt;
&lt;li&gt;JSON includes schema metadata and metrics array&lt;/li&gt;
&lt;li&gt;output order is deterministic&lt;/li&gt;
&lt;li&gt;golden-file tests exist for each format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GitHub Milestone Breakdown
&lt;/h2&gt;

&lt;p&gt;Work is tracked through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;M2-1 canonical metric model&lt;/li&gt;
&lt;li&gt;M2-2 collector-to-canonical mapping&lt;/li&gt;
&lt;li&gt;M2-3 JSON serializer&lt;/li&gt;
&lt;li&gt;M2-4 Prometheus serializer&lt;/li&gt;
&lt;li&gt;M2-5 OpenMetrics serializer&lt;/li&gt;
&lt;li&gt;M2-6 output format config + validation&lt;/li&gt;
&lt;li&gt;M2-7 fixture/contract tests&lt;/li&gt;
&lt;li&gt;M2-8 docs update&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repo
&lt;/h2&gt;

</description>
      <category>telemetry</category>
      <category>opensource</category>
      <category>devops</category>
      <category>python</category>
    </item>
    <item>
      <title>Heka Insights Agent Update: Architecture + Configuration Docs Now Reflect Runtime Behavior</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Thu, 16 Apr 2026 05:37:17 +0000</pubDate>
      <link>https://dev.to/munirfarhan/heka-insights-agent-update-architecture-configuration-docs-now-reflect-runtime-behavior-2e8k</link>
      <guid>https://dev.to/munirfarhan/heka-insights-agent-update-architecture-configuration-docs-now-reflect-runtime-behavior-2e8k</guid>
      <description>&lt;h1&gt;
  
  
  Build Update (April 16, 2026)
&lt;/h1&gt;

&lt;p&gt;This week I focused on documentation quality and operational clarity for &lt;code&gt;heka-insights-agent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The goal was simple: make docs match the code exactly, so contributors and operators can reason about behavior without reading every module first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I updated
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rewrote &lt;code&gt;docs/architecture.md&lt;/code&gt; from scratch&lt;/li&gt;
&lt;li&gt;Rewrote &lt;code&gt;docs/configuration.md&lt;/code&gt; from scratch&lt;/li&gt;
&lt;li&gt;Expanded &lt;code&gt;README.md&lt;/code&gt; with project context, setup, and environment guidance&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture documentation improvements
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;docs/architecture.md&lt;/code&gt; now documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the actual runtime topology and control loop in &lt;code&gt;src/main.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;collector boundaries and behavior (&lt;code&gt;CPUCollector&lt;/code&gt;, &lt;code&gt;MemoryCollector&lt;/code&gt;, &lt;code&gt;DiskCollector&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;logging subsystem behavior in &lt;code&gt;src/logger/config.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;current payload shapes emitted by collectors&lt;/li&gt;
&lt;li&gt;known gaps (no sender layer yet, no tests yet, no schema versioning yet)&lt;/li&gt;
&lt;li&gt;practical extension points for next phases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives a real “as-implemented” architecture baseline instead of aspirational text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration documentation improvements
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;docs/configuration.md&lt;/code&gt; now includes exact behavior for the two active runtime settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LOG_LOCATION&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CPU_POLL_INTERVAL_SECONDS&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source/precedence rules&lt;/li&gt;
&lt;li&gt;defaults and validation behavior&lt;/li&gt;
&lt;li&gt;failure modes&lt;/li&gt;
&lt;li&gt;local setup and production recommendations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Important behavior clarified
&lt;/h2&gt;

&lt;p&gt;Current config loading is split across two env files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;root &lt;code&gt;.env&lt;/code&gt; is used for &lt;code&gt;LOG_LOCATION&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/.env&lt;/code&gt; is used for &lt;code&gt;CPU_POLL_INTERVAL_SECONDS&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That split is now explicitly documented to reduce startup/debug confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;For an agent project, docs are part of reliability.&lt;br&gt;&lt;br&gt;
Operators need to know what can fail at startup, where config is read from, and what telemetry shape to expect downstream.&lt;/p&gt;

&lt;p&gt;This update makes onboarding and future refactors safer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;add transport/sender layer (backend adapters)&lt;/li&gt;
&lt;li&gt;add collector-focused tests&lt;/li&gt;
&lt;li&gt;consolidate config loading into a single source&lt;/li&gt;
&lt;li&gt;define schema/versioning strategy for emitted payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;https://github.com/ronin1770/heka-insights-agent&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>linux</category>
      <category>devops</category>
      <category>observability</category>
    </item>
    <item>
      <title>Reel Quick - Added Docker Support</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Wed, 15 Apr 2026 14:17:25 +0000</pubDate>
      <link>https://dev.to/munirfarhan/reel-quick-added-docker-support-np7</link>
      <guid>https://dev.to/munirfarhan/reel-quick-added-docker-support-np7</guid>
      <description>&lt;p&gt;Date: 2026-04-15&lt;br&gt;&lt;br&gt;
Project: Reel Quick (FastAPI + Next.js + ARQ + Mongo + Redis + optional GPU workers)&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;We containerized the stack and tried to run it in production mode with Docker Compose.&lt;br&gt;&lt;br&gt;
Initial startup failed for both frontend build and backend runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issues Found
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Docker Compose path mismatches:&lt;/li&gt;
&lt;li&gt;Wrong &lt;code&gt;env_file&lt;/code&gt; paths (&lt;code&gt;docker/env/*.env&lt;/code&gt; expected but files were in &lt;code&gt;docker/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Wrong nginx config mount path (&lt;code&gt;./docker/nginx/nginx.conf&lt;/code&gt; while actual file was &lt;code&gt;docker/nginx.conf&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build context was incorrect for a compose file located inside &lt;code&gt;docker/&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend TypeScript build failure:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;location&lt;/code&gt; field type mismatch in &lt;code&gt;frontend/app/create_video/page.tsx&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Value inferred as &lt;code&gt;string | undefined&lt;/code&gt; but state expects &lt;code&gt;string | null&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend container crash on startup:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ModuleNotFoundError: No module named 'db'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;backend/main.py&lt;/code&gt; used non-package imports like &lt;code&gt;from db import ...&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Root Causes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Relative paths in compose were not aligned with actual file layout.&lt;/li&gt;
&lt;li&gt;Optional API response property (&lt;code&gt;file_location?&lt;/code&gt;) was used directly inside state update.&lt;/li&gt;
&lt;li&gt;Backend entrypoint (&lt;code&gt;uvicorn backend.main:app&lt;/code&gt;) requires package-safe imports (&lt;code&gt;backend.*&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fixes Applied
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker and Compose
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Updated &lt;code&gt;docker/docker-compose.yml&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;build.context&lt;/code&gt; changed from &lt;code&gt;.&lt;/code&gt; to &lt;code&gt;..&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env_file&lt;/code&gt; paths corrected to &lt;code&gt;backend.env&lt;/code&gt; and &lt;code&gt;mongo.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;nginx bind mount fixed to &lt;code&gt;./nginx.conf&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Updated &lt;code&gt;docker/backend.env&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Added:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UPLOAD_FILES_LOCATION=/app/video_files&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INPUT_FILES_LOCATION=/app/video_files&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Added repo-root &lt;code&gt;.dockerignore&lt;/code&gt; (Docker uses ignore file from build context root).&lt;/li&gt;

&lt;li&gt;Synced &lt;code&gt;docker/dockerignore&lt;/code&gt; entries.&lt;/li&gt;

&lt;li&gt;Updated &lt;code&gt;docker/README-docker-prod.md&lt;/code&gt; run commands.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fixed type narrowing in &lt;code&gt;frontend/app/create_video/page.tsx&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Captured &lt;code&gt;file_location&lt;/code&gt; into &lt;code&gt;uploadedLocation&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Guarded before &lt;code&gt;setFiles(...)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Used guaranteed string value in state update.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Converted backend imports to package imports in &lt;code&gt;backend/main.py&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;from db import ...&lt;/code&gt; -&amp;gt; &lt;code&gt;from backend.db import ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Similar conversion for &lt;code&gt;logger&lt;/code&gt;, &lt;code&gt;models&lt;/code&gt;, &lt;code&gt;objects&lt;/code&gt;, &lt;code&gt;workers&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Updated &lt;code&gt;backend/objects/sound_prompt_preset.py&lt;/code&gt; import to &lt;code&gt;backend.objects...&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Commands Used for Deploy
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop all running containers (host-wide)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; | xargs &lt;span class="nt"&gt;-r&lt;/span&gt; docker stop

&lt;span class="c"&gt;# Start Reel Quick with GPU workers&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /home/farhan/reel-quick/docker
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; gpu up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker compose ps
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; api &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validation Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker compose ps&lt;/code&gt; shows &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;frontend&lt;/code&gt;, &lt;code&gt;nginx&lt;/code&gt;, &lt;code&gt;mongo&lt;/code&gt;, &lt;code&gt;redis&lt;/code&gt;, workers as running.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api&lt;/code&gt; logs no longer show &lt;code&gt;ModuleNotFoundError: No module named 'db'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Frontend image builds successfully (&lt;code&gt;npm run build&lt;/code&gt; passes in container build stage).&lt;/li&gt;
&lt;li&gt;Upload endpoint works (&lt;code&gt;POST /uploads&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Workers/control panel endpoints return expected data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep compose file paths consistent with its directory and build context.&lt;/li&gt;
&lt;li&gt;Use package-qualified imports for Python app modules in containerized runtimes.&lt;/li&gt;
&lt;li&gt;Narrow optional API fields before state updates in strict TypeScript projects.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;.dockerignore&lt;/code&gt; at the actual build context root to avoid bloated builds.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>python</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Build Log: Implementing Full Text Overlay Feature in Reel Quick (with Accurate Live Preview)</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Fri, 10 Apr 2026 05:46:36 +0000</pubDate>
      <link>https://dev.to/munirfarhan/build-log-implementing-full-text-overlay-feature-in-reel-quick-with-accurate-live-preview-4fcd</link>
      <guid>https://dev.to/munirfarhan/build-log-implementing-full-text-overlay-feature-in-reel-quick-with-accurate-live-preview-4fcd</guid>
      <description>&lt;h1&gt;
  
  
  Build Log: Implementing Full Text Overlay Feature in Reel Quick (with Accurate Live Preview)
&lt;/h1&gt;

&lt;p&gt;In this build, I implemented the complete text overlay workflow in &lt;strong&gt;Reel Quick&lt;/strong&gt;: from UI controls to background processing, plus a preview system that better matches final output.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/ronin1770/reel-quick" rel="noopener noreferrer"&gt;https://github.com/ronin1770/reel-quick&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this feature mattered
&lt;/h2&gt;

&lt;p&gt;The earlier flow allowed adding overlay text, but styling control was limited and preview confidence was low.&lt;br&gt;&lt;br&gt;
Users needed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;control text appearance (size, color)&lt;/li&gt;
&lt;li&gt;control placement (top/center/bottom)&lt;/li&gt;
&lt;li&gt;preview changes instantly before processing&lt;/li&gt;
&lt;li&gt;avoid trial-and-error renders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to make text overlays practical for real reel production, not just a placeholder UI.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the feature now includes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Overlay content + timing
&lt;/h3&gt;

&lt;p&gt;Users can create overlays with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;text&lt;/li&gt;
&lt;li&gt;start time&lt;/li&gt;
&lt;li&gt;end time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Style controls
&lt;/h3&gt;

&lt;p&gt;Added styling inputs in the dialog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Font size&lt;/strong&gt; range: &lt;code&gt;40–200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text color&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;HTML5 color picker (&lt;code&gt;input type="color"&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;HEX input (synced with picker)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Position&lt;/strong&gt; selector:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;top&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;center&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bottom&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Live preview (client-side only)
&lt;/h3&gt;

&lt;p&gt;The overlay updates instantly while editing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no backend call&lt;/li&gt;
&lt;li&gt;no queue call&lt;/li&gt;
&lt;li&gt;no video reprocessing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lets users iterate quickly before clicking process.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Dialog UX improvements
&lt;/h3&gt;

&lt;p&gt;The modal was redesigned to be usable at production scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;controls on the left&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;preview on the right&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;scrollable modal for smaller viewports&lt;/li&gt;
&lt;li&gt;action buttons always reachable&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The key technical challenge: preview/output mismatch
&lt;/h2&gt;

&lt;p&gt;A big issue was that selected preview size didn’t visually match rendered output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root cause
&lt;/h3&gt;

&lt;p&gt;Frontend preview text used raw CSS px in a display container, while backend renders text on actual output video resolution.&lt;br&gt;&lt;br&gt;
Same number, different render context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix strategy
&lt;/h3&gt;

&lt;p&gt;I changed preview scaling logic to account for real video dimensions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read intrinsic source dimensions from video metadata (&lt;code&gt;videoWidth&lt;/code&gt;, &lt;code&gt;videoHeight&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Measure actual preview frame dimensions in the modal&lt;/li&gt;
&lt;li&gt;Compute scale factor:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scale = min(previewWidth/sourceWidth, previewHeight/sourceHeight)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Render preview text as:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;previewFontSize = selectedFontSize * scale&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Preserve source aspect ratio in preview container&lt;/li&gt;
&lt;li&gt;Mirror vertical placement behavior (top/center/bottom with scaled edge padding)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Result: preview size and placement now feel much closer to final rendered video.&lt;/p&gt;




&lt;h2&gt;
  
  
  API + backend integration
&lt;/h2&gt;

&lt;p&gt;Good news: backend already supported style fields, so no backend API redesign was needed.&lt;/p&gt;

&lt;p&gt;The frontend sends per-overlay payload with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;style.font_size&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;style.text_color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;position.preset&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it follows the existing pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save overlays
&lt;code&gt;POST /videos/{video_id}/text-overlays&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enqueue processing
&lt;code&gt;POST /enqueue/text-overlay&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ARQ worker picks job and runs MoviePy text overlay composition&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Data flow (end to end)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User opens text overlay dialog&lt;/li&gt;
&lt;li&gt;Configures text + timing + style + position&lt;/li&gt;
&lt;li&gt;Verifies in live preview&lt;/li&gt;
&lt;li&gt;Saves overlay config&lt;/li&gt;
&lt;li&gt;Enqueues job&lt;/li&gt;
&lt;li&gt;Worker validates and renders final video&lt;/li&gt;
&lt;li&gt;Processed text-overlay video becomes available for download&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Validation and guardrails
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Font size is clamped to configured range&lt;/li&gt;
&lt;li&gt;HEX color is normalized/validated&lt;/li&gt;
&lt;li&gt;Overlay timing is validated before processing&lt;/li&gt;
&lt;li&gt;Position options are constrained to supported presets&lt;/li&gt;
&lt;li&gt;Preview updates remain instant and local&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What changed from earlier behavior
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Old frontend default style used a very small fixed size
&lt;/li&gt;
&lt;li&gt;New feature provides interactive style control + accurate preview scaling
&lt;/li&gt;
&lt;li&gt;Modal UX is now horizontal and production-friendly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;Potential follow-ups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;font family selection&lt;/li&gt;
&lt;li&gt;stroke/shadow controls in UI&lt;/li&gt;
&lt;li&gt;drag-and-drop custom placement&lt;/li&gt;
&lt;li&gt;multiple overlay tracks/timeline editing&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you want, I can also generate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a shorter Dev.to teaser version&lt;/li&gt;
&lt;li&gt;an accompanying screenshot checklist for the post&lt;/li&gt;
&lt;li&gt;a “before vs after” section with technical diff notes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvyp7cxlj3rpl08ea7ap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvyp7cxlj3rpl08ea7ap.png" alt=" " width="800" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4619l5msmkovj9ahxgxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4619l5msmkovj9ahxgxf.png" alt=" " width="800" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>nextjs</category>
      <category>fastapi</category>
      <category>moviepy</category>
    </item>
    <item>
      <title>Build Log: Shipping a Lean Python Telemetry Agent (CPU, Memory, Disk)</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Wed, 08 Apr 2026 09:29:51 +0000</pubDate>
      <link>https://dev.to/munirfarhan/build-log-shipping-a-lean-python-telemetry-agent-cpu-memory-disk-30j1</link>
      <guid>https://dev.to/munirfarhan/build-log-shipping-a-lean-python-telemetry-agent-cpu-memory-disk-30j1</guid>
      <description>&lt;h1&gt;
  
  
  Build Log (April 8, 2026)
&lt;/h1&gt;

&lt;p&gt;Today I implemented the first production-ready telemetry collectors for &lt;code&gt;heka-insights-agent&lt;/code&gt; and wired them into the main polling loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Added an optimized &lt;code&gt;CPUCollector&lt;/code&gt; in &lt;code&gt;src/collectors/cpu.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;MemoryCollector&lt;/code&gt; in &lt;code&gt;src/collectors/memory.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;DiskCollector&lt;/code&gt; in &lt;code&gt;src/collectors/disk.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wired all collectors into &lt;code&gt;src/main.py&lt;/code&gt; with a shared loop&lt;/li&gt;
&lt;li&gt;Added environment-based poll interval support via &lt;code&gt;CPU_POLL_INTERVAL_SECONDS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;python-dotenv&lt;/code&gt; in &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CPU collector design
&lt;/h2&gt;

&lt;p&gt;I built CPU collection around &lt;code&gt;psutil.cpu_times(...)&lt;/code&gt; snapshots and delta math (single source), instead of calling both &lt;code&gt;cpu_percent&lt;/code&gt; and &lt;code&gt;cpu_times_percent&lt;/code&gt; per cycle.&lt;/p&gt;

&lt;p&gt;Key design points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No thread offloading (&lt;code&gt;to_thread&lt;/code&gt;) for this workload&lt;/li&gt;
&lt;li&gt;First cycle is warm-up by design&lt;/li&gt;
&lt;li&gt;Supports &lt;code&gt;basic&lt;/code&gt; and &lt;code&gt;detailed&lt;/code&gt; output modes&lt;/li&gt;
&lt;li&gt;Optional per-core output&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;MonotonicTicker&lt;/code&gt; to keep fixed cadence without drift&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Memory collector design
&lt;/h2&gt;

&lt;p&gt;Memory collection is intentionally lightweight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One call each to &lt;code&gt;psutil.virtual_memory()&lt;/code&gt; and &lt;code&gt;psutil.swap_memory()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;basic&lt;/code&gt; mode returns compact key fields&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;detailed&lt;/code&gt; mode returns full psutil fields&lt;/li&gt;
&lt;li&gt;Raw byte values are preserved (server-side compute handles transformations)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Disk collector design
&lt;/h2&gt;

&lt;p&gt;For disk, I chose cumulative I/O counters (not rates) because central compute is done server-side.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;code&gt;psutil.disk_io_counters(perdisk=True)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Returns aggregate and per-disk counters&lt;/li&gt;
&lt;li&gt;Filters to physical devices only&lt;/li&gt;
&lt;li&gt;Excludes partitions from per-disk payload&lt;/li&gt;
&lt;li&gt;Added device-name cache with periodic refresh to reduce repeated filtering overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Main loop wiring
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;src/main.py&lt;/code&gt; now runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU collector&lt;/li&gt;
&lt;li&gt;Memory collector&lt;/li&gt;
&lt;li&gt;Disk collector&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All on the same interval, with separate log lines per collector.&lt;/p&gt;

&lt;p&gt;Poll interval is loaded from &lt;code&gt;.env&lt;/code&gt; via:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CPU_POLL_INTERVAL_SECONDS&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Invalid values fall back safely to default &lt;code&gt;5.0s&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling notes
&lt;/h2&gt;

&lt;p&gt;I profiled a 120-second run and reviewed both process stats and cProfile output.&lt;/p&gt;

&lt;p&gt;Key findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent CPU cost is very low (near-idle for this polling interval)&lt;/li&gt;
&lt;li&gt;Max RSS is about 15 MB&lt;/li&gt;
&lt;li&gt;Runtime is dominated by intentional sleep (expected)&lt;/li&gt;
&lt;li&gt;Collector costs are small; disk collection is the heaviest of the three&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What changed after profiling
&lt;/h2&gt;

&lt;p&gt;Based on profile output, I optimized disk collection further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added cached physical-device list to avoid filtering every cycle&lt;/li&gt;
&lt;li&gt;Kept output shape unchanged (&lt;code&gt;disk_io&lt;/code&gt; + &lt;code&gt;disk_io_perdisk&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current status
&lt;/h2&gt;

&lt;p&gt;The agent now has a clean baseline telemetry pipeline with low overhead and clear extension points for transport/shipping.&lt;/p&gt;

&lt;p&gt;Next planned work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add payload shipping to backend endpoint&lt;/li&gt;
&lt;li&gt;Add bounded retry/backoff&lt;/li&gt;
&lt;li&gt;Add collector-focused tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Repo URL
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ronin1770" rel="noopener noreferrer"&gt;
        ronin1770
      &lt;/a&gt; / &lt;a href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;
        heka-insights-agent
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A lightweight agent for collecting essential Linux system telemetry and shipping it to a configurable backend.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;heka-insights-agent&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A lightweight agent for collecting essential Linux system telemetry and shipping it to a configurable backend.&lt;/p&gt;
&lt;p&gt;Test&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ronin1770/heka-insights-agent" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>python</category>
      <category>monitoring</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>Build Log: End-to-End Text Overlay Workflow for Video Processing</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Thu, 02 Apr 2026 16:04:23 +0000</pubDate>
      <link>https://dev.to/munirfarhan/build-log-end-to-end-text-overlay-workflow-for-video-processing-14ie</link>
      <guid>https://dev.to/munirfarhan/build-log-end-to-end-text-overlay-workflow-for-video-processing-14ie</guid>
      <description>&lt;h1&gt;
  
  
  Build Log: End-to-End Text Overlay Workflow for Video Processing
&lt;/h1&gt;

&lt;p&gt;Today I completed the core text overlay workflow across the frontend, backend, and worker pipeline. The main goal was to make text overlays behave like a real production feature instead of a partial UI action. That meant saving overlays, enqueueing processing jobs, running worker execution, tracking status, and making sure users can download the final processed file when rendering is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;The biggest update was implementing the full text overlay processing lifecycle from the UI down to the background worker.&lt;/p&gt;

&lt;p&gt;A user can now create overlays for a video, save them through the API, enqueue a rendering job, and return to the videos page while processing continues in the background. Once the job is finished, the videos listing reflects that state and download actions point to the processed overlay output instead of the original file.&lt;/p&gt;

&lt;p&gt;I also finalized one important behavior change in overlay saving. Instead of merging new overlays with existing ones, each save call now replaces the stored overlays for that video. This prevents stale overlay entries from lingering and causing recurring overlap conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend work completed
&lt;/h2&gt;

&lt;p&gt;On the backend, I added the API and queue flow required to support the feature end to end.&lt;/p&gt;

&lt;p&gt;The following routes are now in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /videos/{video_id}/text-overlays&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /enqueue/text-overlay&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /text-overlay-jobs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /videos/{video_id}/text-overlays/download&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The save endpoint now stores the submitted overlays for a given video. The enqueue endpoint pushes a text overlay processing job into the queue and returns quickly, so the frontend does not need to wait on worker readiness before moving forward.&lt;/p&gt;

&lt;p&gt;To support job visibility, I introduced a &lt;code&gt;text_overlay_jobs&lt;/code&gt; collection for tracking processing records. This collection is now used for queued, pending, finished, and failure state tracking. I also added the required database initialization and an index on &lt;code&gt;text_overlay_jobs.video_id&lt;/code&gt; so lookup remains efficient.&lt;/p&gt;

&lt;p&gt;On the worker side, I added &lt;code&gt;process_text_overlay_job&lt;/code&gt; and wired a dedicated &lt;code&gt;text_overlay_worker&lt;/code&gt; into the queue system using &lt;code&gt;TEXT_OVERLAY_QUEUE_NAME&lt;/code&gt;. That worker is also registered in the control panel worker setup so it is managed consistently with the rest of the background processing stack.&lt;/p&gt;

&lt;p&gt;Another important backend fix was changing overlay persistence behavior from merge to replace. Earlier behavior allowed stale overlays to remain in the record, which could trigger overlap validation conflicts even after the user thought they had corrected the layout. Replacing overlays on save solved that problem cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend updates
&lt;/h2&gt;

&lt;p&gt;On the frontend, the &lt;code&gt;CreateTextOverlayPage&lt;/code&gt; is now connected to the real processing flow.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Done / Process Video&lt;/strong&gt; action now performs the expected sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save overlays&lt;/li&gt;
&lt;li&gt;Enqueue the text overlay job&lt;/li&gt;
&lt;li&gt;Redirect the user back to &lt;code&gt;/videos&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sfzla57dmdsoghbijnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sfzla57dmdsoghbijnl.png" alt=" " width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also added a loading and disabled state to prevent duplicate submissions while the request is in progress. This gives the action more predictable UX and reduces accidental repeat clicks.&lt;/p&gt;

&lt;p&gt;Error handling was improved as well. Instead of showing vague failures, the frontend now parses backend validation messages more clearly, including field paths where possible. That makes it much easier to debug malformed overlay payloads during testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowt8qtc79ur3fvsaopyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowt8qtc79ur3fvsaopyw.png" alt=" " width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because some UI controls are still not finalized, I added temporary default values for fields that are not yet exposed in the interface. Current defaults are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;position: top/center&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;style: font size 16, weight normal&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These defaults let the feature move forward without blocking on the final design of overlay controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Videos page behavior
&lt;/h2&gt;

&lt;p&gt;I also updated the &lt;code&gt;/videos&lt;/code&gt; listing page so the new workflow feels integrated instead of isolated.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Video ID&lt;/strong&gt; column was removed to simplify the table and give more room to user-facing actions.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Text Overlay&lt;/strong&gt; column now reflects job state more meaningfully. If the overlay job for a video is finished, the action shows &lt;strong&gt;Done&lt;/strong&gt;. Otherwise, it shows &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Download behavior was also updated. When an overlay job is finished successfully, the output and error download actions now point to the processed overlay video instead of the original file. If overlay processing has not completed, downloads continue to use the regular video file.&lt;/p&gt;

&lt;p&gt;That small change is important because it makes the final output feel connected to the workflow the user just triggered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the replace-on-save decision mattered
&lt;/h2&gt;

&lt;p&gt;One of the more important logic decisions today was changing overlay saving from merge semantics to replace semantics.&lt;/p&gt;

&lt;p&gt;Merging sounds convenient at first, but in practice it created stale state problems. Old overlays could remain attached to the video even after the user thought they were working from a clean set. That caused overlap validation issues to reappear and made the experience feel inconsistent.&lt;/p&gt;

&lt;p&gt;Replacing overlays on each save call makes the system much more predictable. The saved overlays now reflect exactly what the user submitted in the latest request, which is the safer behavior for this kind of editor flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current workflow
&lt;/h2&gt;

&lt;p&gt;At this point, the text overlay feature supports the following flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User opens the text overlay page for a video&lt;/li&gt;
&lt;li&gt;User adds or edits overlays&lt;/li&gt;
&lt;li&gt;Frontend saves the overlays&lt;/li&gt;
&lt;li&gt;Frontend enqueues a processing job&lt;/li&gt;
&lt;li&gt;Worker renders the text overlay output in the background&lt;/li&gt;
&lt;li&gt;Job status is tracked in &lt;code&gt;text_overlay_jobs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Videos page reflects completion state&lt;/li&gt;
&lt;li&gt;Download action serves the overlay-rendered file when available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the feature is now functional across storage, processing, status tracking, and output retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still temporary
&lt;/h2&gt;

&lt;p&gt;A few parts are still intentionally temporary or transitional.&lt;/p&gt;

&lt;p&gt;The overlay editor is still using fallback defaults for position and style where dedicated UI controls have not been built yet. The current implementation is enough to keep the workflow functional, but the page still needs the final interactive form controls for layout and style management.&lt;/p&gt;

&lt;p&gt;The videos page is also currently showing a simplified status signal. This works for now, but it could later become more expressive with states like queued, processing, failed, and finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;This was a solid progress day because the feature moved from partial UI scaffolding into a real end-to-end system. The frontend now triggers meaningful backend work, the worker actually processes jobs, job state is persisted, and the final output can be downloaded through the app.&lt;/p&gt;

&lt;p&gt;Most importantly, the workflow now behaves in a predictable way. Saving overlays replaces prior state, queueing does not block on hard worker health checks, and completed processing is reflected directly in the videos list.&lt;/p&gt;

&lt;p&gt;The next step is to improve the overlay editor UI itself, but the underlying pipeline is now in place.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>buildinpublic</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
