<?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>Heka-Insights-Agent: Milestone 6 Complete: Datadog OTLP + Native Integration Paths</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Sun, 17 May 2026 06:04:48 +0000</pubDate>
      <link>https://dev.to/munirfarhan/heka-insights-agent-milestone-6-complete-datadog-otlp-native-integration-paths-4144</link>
      <guid>https://dev.to/munirfarhan/heka-insights-agent-milestone-6-complete-datadog-otlp-native-integration-paths-4144</guid>
      <description>&lt;p&gt;Milestone 6 is complete for the Heka Insights Agent.&lt;/p&gt;

&lt;p&gt;Repository: &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;p&gt;In this milestone, we implemented &lt;strong&gt;two Datadog-compatible delivery paths&lt;/strong&gt; so teams can choose what fits their needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Datadog OTLP preset mode&lt;/strong&gt; (&lt;code&gt;EXPORTER_TYPE=datadog_otlp&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datadog native API mode&lt;/strong&gt; (&lt;code&gt;EXPORTER_TYPE=datadog_native&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Milestone 6 Goal
&lt;/h2&gt;

&lt;p&gt;Provide Datadog integration through both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OTLP-based delivery for portability&lt;/li&gt;
&lt;li&gt;Datadog-native delivery for backend-specific control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In common terms: this means your agent can now send metrics to Datadog in either a standards-oriented way (OTLP) or a Datadog-specific way (native API), without changing your collectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Implemented
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Datadog OTLP preset mode (&lt;code&gt;datadog_otlp&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;We added a Datadog preset resolver that derives endpoint + auth headers from Datadog config.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;endpoint is derived from site: &lt;code&gt;https://otlp.&amp;lt;DATADOG_SITE&amp;gt;/v1/metrics&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;auth header is injected: &lt;code&gt;dd-api-key: &amp;lt;DATADOG_API_KEY&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;optional Datadog hostname/tags are mapped into resource attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Datadog native exporter (&lt;code&gt;datadog_native&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;We implemented a native Datadog metrics exporter for &lt;code&gt;POST /api/v1/series&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;endpoint is derived from site: &lt;code&gt;https://api.&amp;lt;DATADOG_SITE&amp;gt;/api/v1/series&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;canonical &lt;code&gt;gauge&lt;/code&gt; -&amp;gt; Datadog &lt;code&gt;gauge&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;canonical &lt;code&gt;counter&lt;/code&gt; -&amp;gt; Datadog &lt;code&gt;count&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;timestamp_unix_ms&lt;/code&gt; -&amp;gt; Unix seconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;count.interval&lt;/code&gt; is derived from &lt;code&gt;CPU_POLL_INTERVAL_SECONDS&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Validation hardening (M6-3)
&lt;/h3&gt;

&lt;p&gt;We fail fast on invalid/missing Datadog config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATADOG_SITE&lt;/code&gt; must be an allowed full domain&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DATADOG_API_KEY&lt;/code&gt; must be non-empty&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DATADOG_TAGS&lt;/code&gt; must be strict &lt;code&gt;key:value&lt;/code&gt; format&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Deterministic mapping rules (M6-4)
&lt;/h3&gt;

&lt;p&gt;We made mapping predictable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DATADOG_HOSTNAME&lt;/code&gt; overrides label-derived host&lt;/li&gt;
&lt;li&gt;tag conflicts are deterministic (&lt;code&gt;DATADOG_TAGS&lt;/code&gt; override label tags by key)&lt;/li&gt;
&lt;li&gt;metric prefixing is idempotent (won't double-prefix)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. OTLP vs native comparison docs (M6-5)
&lt;/h3&gt;

&lt;p&gt;We added side-by-side docs so teams can quickly decide mode based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transport style&lt;/li&gt;
&lt;li&gt;portability&lt;/li&gt;
&lt;li&gt;mapping behavior&lt;/li&gt;
&lt;li&gt;counter interval semantics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Docker-based milestone-6 integration tests (M6-6)
&lt;/h3&gt;

&lt;p&gt;We added dedicated tests under:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tests/milestone-6/test_datadog_live_integration.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are intentionally gated and only run when explicitly enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Files Added/Updated
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/config/runtime.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/config/__init__.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/exporters/factory.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/exporters/datadog_native.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tests/test_config_otlp_env.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tests/test_datadog_exporters.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tests/milestone-6/test_datadog_live_integration.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs/configuration.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs/architecture.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs/development.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;h2&gt;
  
  
  Implementation Strategy
&lt;/h2&gt;

&lt;p&gt;We followed the existing exporter architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep collectors unchanged&lt;/li&gt;
&lt;li&gt;Extend runtime config with strict validation and preset resolution&lt;/li&gt;
&lt;li&gt;Reuse OTLP HTTP exporter for Datadog OTLP mode&lt;/li&gt;
&lt;li&gt;Add a dedicated native exporter for Datadog API v1 series&lt;/li&gt;
&lt;li&gt;Add deterministic mapping rules + comprehensive tests&lt;/li&gt;
&lt;li&gt;Add Docker-backed live tests in a milestone-specific folder&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test Commands and Outputs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Local Datadog-focused test suite
&lt;/h3&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 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  tests.test_config_otlp_env &lt;span class="se"&gt;\&lt;/span&gt;
  tests.test_datadog_exporters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full local suite
&lt;/h3&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;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

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

OK (skipped=1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker live Datadog integration tests (milestone-6)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUN_OTLP_INTEGRATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUN_DATADOG_LIVE_INTEGRATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DATADOG_SITE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us5.datadoghq.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DATADOG_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;REDACTED_DATADOG_API_KEY&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  test-runner &lt;span class="se"&gt;\&lt;/span&gt;
  pytest &lt;span class="nt"&gt;-vv&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-rs&lt;/span&gt; tests/milestone-6/test_datadog_live_integration.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observed output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collected 2 items

tests/milestone-6/test_datadog_live_integration.py::DatadogLiveIntegrationTests::test_datadog_native_exports_gauge_and_count_metrics PASSED
tests/milestone-6/test_datadog_live_integration.py::DatadogLiveIntegrationTests::test_datadog_otlp_preset_exports_gauge_metric PASSED

2 passed in 2.63s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;With Milestone 6 complete, Datadog users can now choose the integration path that matches their operations model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose &lt;strong&gt;OTLP preset&lt;/strong&gt; for standards-aligned portability&lt;/li&gt;
&lt;li&gt;choose &lt;strong&gt;native mode&lt;/strong&gt; for Datadog-specific control and semantics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both paths are now validated, tested, and documented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contribute
&lt;/h2&gt;

&lt;p&gt;If you're interested in observability agents, OTLP pipelines, or exporter design, contributions are welcome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&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;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful contribution areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;additional Datadog live integration coverage&lt;/li&gt;
&lt;li&gt;CI automation for gated integration scenarios&lt;/li&gt;
&lt;li&gt;docs/examples for production deployments&lt;/li&gt;
&lt;li&gt;future milestone reliability and operability improvements&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>datadog</category>
      <category>telemetry</category>
    </item>
    <item>
      <title>Milestone 5 Complete: New Relic OTLP Integration for Heka Insights Agent</title>
      <dc:creator>Farhan Munir</dc:creator>
      <pubDate>Tue, 12 May 2026 05:33:12 +0000</pubDate>
      <link>https://dev.to/munirfarhan/milestone-5-complete-new-relic-otlp-integration-for-heka-insights-agent-411f</link>
      <guid>https://dev.to/munirfarhan/milestone-5-complete-new-relic-otlp-integration-for-heka-insights-agent-411f</guid>
      <description>&lt;p&gt;In this milestone, we focused on making &lt;strong&gt;New Relic integration first-class&lt;/strong&gt; in the Heka Insights Agent, while still using the same OTLP HTTP exporter foundation.&lt;/p&gt;

&lt;p&gt;Repository: &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;
  
  
  Milestone 5 Goal
&lt;/h2&gt;

&lt;p&gt;Milestone 5 was about shipping a reliable, low-friction New Relic path without creating a separate proprietary exporter.&lt;/p&gt;

&lt;p&gt;Scope covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New Relic preset configuration layer&lt;/li&gt;
&lt;li&gt;Automatic New Relic auth header injection&lt;/li&gt;
&lt;li&gt;Endpoint and required field validation&lt;/li&gt;
&lt;li&gt;Documentation updates with practical examples&lt;/li&gt;
&lt;li&gt;Integration tests for preset behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What We Changed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. New Relic preset mode (&lt;code&gt;EXPORTER_TYPE=newrelic_otlp&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;We added a preset resolver that maps New Relic-specific environment variables into OTLP HTTP exporter inputs.&lt;/p&gt;

&lt;p&gt;Required variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NEWRELIC_OTLP_ENDPOINT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NEWRELIC_API_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NEWRELIC_SERVICE_NAME&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Optional variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NEWRELIC_ENVIRONMENT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NEWRELIC_HOST_NAME&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Automatic auth header injection
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;EXPORTER_TYPE=newrelic_otlp&lt;/code&gt; is selected, the runtime now injects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;api-key: &amp;lt;NEWRELIC_API_KEY&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No manual &lt;code&gt;OTLP_HTTP_HEADERS&lt;/code&gt; setup is required for baseline New Relic auth.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Precedence behavior
&lt;/h3&gt;

&lt;p&gt;In preset mode, &lt;code&gt;NEWRELIC_*&lt;/code&gt; values take precedence over conflicting generic &lt;code&gt;OTLP_*&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NEWRELIC_API_KEY&lt;/code&gt; overrides any conflicting &lt;code&gt;OTLP_HTTP_HEADERS&lt;/code&gt; &lt;code&gt;api-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NEWRELIC_SERVICE_NAME&lt;/code&gt; overrides &lt;code&gt;service.name&lt;/code&gt; from &lt;code&gt;OTLP_RESOURCE_ATTRIBUTES&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Validation hardening
&lt;/h3&gt;

&lt;p&gt;We now fail fast on invalid or missing required New Relic settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing required keys -&amp;gt; startup error&lt;/li&gt;
&lt;li&gt;invalid endpoint format -&amp;gt; startup error (must be absolute &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Documentation updates
&lt;/h3&gt;

&lt;p&gt;We updated project docs and examples to include New Relic preset usage and expected test flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands to Run Tests
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unit tests for config behavior
&lt;/h3&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 &lt;span class="nt"&gt;-v&lt;/span&gt; tests.test_config_otlp_env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New Relic Docker integration tests (explicit)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUN_OTLP_INTEGRATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;OTLP_IT_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host.docker.internal &lt;span class="se"&gt;\&lt;/span&gt;
  test-runner &lt;span class="se"&gt;\&lt;/span&gt;
  pytest &lt;span class="nt"&gt;-vv&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-rs&lt;/span&gt; tests/milestone-5/test_newrelic_otlp_integration.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected summary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collected 3 items
...
3 passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full OTLP/HTTP Docker test stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;--abort-on-container-exit&lt;/span&gt; &lt;span class="nt"&gt;--exit-code-from&lt;/span&gt; test-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;With Milestone 5 complete, teams can onboard New Relic using a predictable preset path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;less config ambiguity&lt;/li&gt;
&lt;li&gt;safer startup behavior through validation&lt;/li&gt;
&lt;li&gt;standardized OTLP transport path&lt;/li&gt;
&lt;li&gt;stronger confidence through Docker-backed integration tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next steps will continue building on this exporter foundation while keeping the runtime predictable and vendor-friendly.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>telemetry</category>
      <category>agents</category>
    </item>
    <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>
  </channel>
</rss>
