<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Bit by Bit]]></title><description><![CDATA[Thoughts on AI, technology, and humans. All opinions are my own.

]]></description><link>https://bit.kevinslin.com</link><image><url>https://substackcdn.com/image/fetch/$s_!orWs!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5c8ceab-5be4-4153-a13d-e906add1fbda_512x512.png</url><title>Bit by Bit</title><link>https://bit.kevinslin.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 20 Apr 2026 15:16:10 GMT</lastBuildDate><atom:link href="https://bit.kevinslin.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Kevin Lin]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[kevin@dendron.so]]></webMaster><itunes:owner><itunes:email><![CDATA[kevin@dendron.so]]></itunes:email><itunes:name><![CDATA[Kevin Lin]]></itunes:name></itunes:owner><itunes:author><![CDATA[Kevin Lin]]></itunes:author><googleplay:owner><![CDATA[kevin@dendron.so]]></googleplay:owner><googleplay:email><![CDATA[kevin@dendron.so]]></googleplay:email><googleplay:author><![CDATA[Kevin Lin]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Thoughts on Claude Skills]]></title><description><![CDATA[In case you missed the launch announcement - skills are structured markdown files with (optional) resources that can augment LLM&#8217;s with additional capabilities (eg.]]></description><link>https://bit.kevinslin.com/p/skills-as-object-oriented-programming</link><guid isPermaLink="false">https://bit.kevinslin.com/p/skills-as-object-oriented-programming</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Wed, 12 Nov 2025 16:01:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!puaL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!puaL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!puaL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!puaL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!puaL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!puaL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!puaL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2008183,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://bit.kevinslin.com/i/178667647?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!puaL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!puaL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!puaL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!puaL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d00dd99-c0d0-44ed-9d77-2a37028200c2_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>In case you missed the <a href="https://www.claude.com/blog/skills">launch announcement</a> - skills are structured markdown files with (optional) resources that can augment LLM&#8217;s with additional capabilities (eg. create a power point, run a financial analysis, etc).</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>A skill is defined by a SKILL.md file with specific frontmatter. A full functioning example below:</p><pre><code>---
name: hello
description: A greetings skill. Invoke when the user gives salutations.
---

Enthusiastically say hi to the users. Use lots of emojis!</code></pre><p>This skill can be further extended by bundling additional files with the main skill file.</p><pre><code>hello/
&#9500;&#9472;&#9472; SKILL.md (main instructions)
&#9500;&#9472;&#9472; EMOJIS.md (catalogue of emojis to use)
&#9492;&#9472;&#9472; scripts/
    &#9492;&#9472;&#9472; animate.py (utility script that can animate emojis)</code></pre><p>Skills have completely taken over the majority of my llm based workflows and its hard now to imagine working without them. They represent the most exciting step forward for augmenting LLM capability (in user space) since the introduction of MCPs (but with real use cases).</p><h2>Priors</h2><p>Prior to skills, every new conversation with a LLM felt like a scene from <a href="https://en.wikipedia.org/wiki/50_First_Dates">50 first dates</a>.</p><p>Yes, we had standards like <a href="https://pnote.eu/notes/agents-md/">AGENTS.md</a> and <a href="https://www.anthropic.com/news/model-context-protocol">MCPs</a> to help customize LLMs. The former helped steer LLMs using a global README file, the latter helped augment LLM capability through the use of arbitrary tools. But neither approach was scalable as the number of workflows and tools kept increasing.</p><p>Stuffing everything in a giant AGENTS.md file eventually leads to context issues and impacts model performance on unrelated tasks. MCPs helped encapsulate steps in a workflow but introduced the additional overhead of requiring code execution. They also run into context issues as the number of MCPs increase because the LLM will load the entire function signature of each MCP (some of which can consume thousands of tokens) into context at the start of every conversation.</p><h2>Skills</h2><p>Enter skills. Because it is just a markdown file, it inherits the simplicity of the AGENTS.md file. Because the LLM only reads the title and description instead of the entire skill in context when starting a conversation, it is extremely token efficient. And because skills can bundle in code and additional resources, it means you get the full power of MCPs as well as anything else you care to bundle.</p><p>If we think of prompts as procedural code (execute said logic from top to bottom) and MCPs as function calls (encapsulation of related logic), then skills represent <a href="https://en.wikipedia.org/wiki/Object-oriented_programming">object oriented programming</a>.</p><p>Skills give prompts the necessary structure to let us compose existing LLM primitives into larger and more ambitious constructs.  This means end users no longer need to wait for model providers to train smarter models to unlock additional capability - they can build skills instead.</p><h2>What&#8217;s Next</h2><p>Skills are great but they do have some limitations. Most notably, they are only available for Claude and have no built-in sharing mechanism. I ended up building a CLI (<a href="https://github.com/kevinslin/skillz">skillz</a>) to solve the availability issue for my own workflows - it works by injecting skill instructions into codex and other LLM powered coding tools. </p><p>Just as object-oriented programming transformed software development by making code reusable and composable, skills represent a similar inflection point for LLMs. We&#8217;re moving from a world where new capabilities required model retraining to one where users can extend and specialize their AI tools through composition. The next few years won&#8217;t just be about bigger models&#8212;they&#8217;ll be about richer ecosystems, better sharing mechanisms, and new abstractions we haven&#8217;t even imagined yet.</p><p>If you&#8217;re still copy-pasting prompts or struggling with context limits, consider trying out skills.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Scaling Stateful Aggregations]]></title><description><![CDATA[Leveraging Vector and VRL for Efficient Log Aggregation]]></description><link>https://bit.kevinslin.com/p/scaling-stateful-aggregations</link><guid isPermaLink="false">https://bit.kevinslin.com/p/scaling-stateful-aggregations</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Sun, 05 May 2024 13:20:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!W5pB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W5pB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W5pB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 424w, https://substackcdn.com/image/fetch/$s_!W5pB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 848w, https://substackcdn.com/image/fetch/$s_!W5pB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 1272w, https://substackcdn.com/image/fetch/$s_!W5pB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W5pB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A fantastical depiction of the internal workings of a sophisticated mechanical watch. The image features an intricate design with gears, springs, and cogs visible in exquisite detail. The mechanism should appear almost magical, with tiny lights glowing from within, adding an ethereal quality to the scene. The overall look should be hyper-realistic, emphasizing the complexity and beauty of watch mechanics, as if viewed through a magnifying glass.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A fantastical depiction of the internal workings of a sophisticated mechanical watch. The image features an intricate design with gears, springs, and cogs visible in exquisite detail. The mechanism should appear almost magical, with tiny lights glowing from within, adding an ethereal quality to the scene. The overall look should be hyper-realistic, emphasizing the complexity and beauty of watch mechanics, as if viewed through a magnifying glass." title="A fantastical depiction of the internal workings of a sophisticated mechanical watch. The image features an intricate design with gears, springs, and cogs visible in exquisite detail. The mechanism should appear almost magical, with tiny lights glowing from within, adding an ethereal quality to the scene. The overall look should be hyper-realistic, emphasizing the complexity and beauty of watch mechanics, as if viewed through a magnifying glass." srcset="https://substackcdn.com/image/fetch/$s_!W5pB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 424w, https://substackcdn.com/image/fetch/$s_!W5pB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 848w, https://substackcdn.com/image/fetch/$s_!W5pB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 1272w, https://substackcdn.com/image/fetch/$s_!W5pB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F724c7749-6bd3-4c1c-87bf-60fa53a94a46_1024x1024.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a distributed system, state is akin to original sin - we can't help having it somewhere and it causes all sorts of problems because it's there. This is especially true for distributed state - that is context that needs to be shared across multiple nodes in a system. In this post, we dive into how we manage state at <a href="https://www.nimbus.dev/">Nimbus</a> by leveraging <a href="https://vector.dev/">Vector</a>, a high-performance tool for building observability pipelines, to optimize log aggregations while ensuring availability and scalability.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit (by Nimbus)! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Context</h2><p>Nimbus help customers optimize their log volume by aggregating <em>similar logs</em> into an aggregate log. During this process, Nimbus reduces, on average, log volume by 80% and log size by 30%. When aggregating logs, a subset of aggregations are stateful - that it to say it matters what node an aggregation is routed to.</p><p>Take the simplified example of logs with a common trace id:</p><pre><code><code>{trace_id:1, msg: start checkout, timestamp: 1}
{trace_id:1, msg: apply discount, timestamp: 2}
{trace_id:1, msg: verify payment, timestamp: 3}
{trace_id:1, msg: complete transaction, timestamp: 4}</code></code></pre><p>The same logs after stateful aggregation (aka all logs being processed by the same node):</p><pre><code><code>{trace_id:1, msg: [start checkout, apply discount, verify payment, complete transaction], timestamp: 1, timestamp_end: 4, size: 4}</code></code></pre><p>Note that during the aggregation, we drop the individual timestamps and keep just the start and end timestamp of the group. If aggregation were not stateful, it can result in the logs being split across multiple aggregations.</p><p>An example of the same logs without stateful aggregation:</p><pre><code><code>{trace_id:1, msg: [apply discount, verify payment ], timestamp: 1, timestamp_end: 3, size: 2}
{trace_id:1, msg: [start checkout, complete transaction], timestamp: 2, timestamp_end: 4, size: 2}</code></code></pre><p>Not only is the latter result less efficient in terms of size and volume reduction, it also causes operational issues. Because logs are now interleaved between groups, it is hard to see the proper sequence of events and make it difficult to do log based monitors and dashboards.</p><p>One way of getting around this problem is to run a single node to do all aggregations. This can get you <a href="https://medium.com/ibm-cloud/log-collectors-performance-benchmarking-8c5218a08fea">quite far</a> but come with scalability, availability and durability issues due to having a single point of failure. Another solution is to accept sub-optimal log aggregations and letting any node manage any aggregation. But if neither of these options are acceptable, then the system itself needs to coordinate traffic so that logs that belong to a common aggregation are processed by the same node. This system is what we'll be describing in this post.</p><h2>Nimbus Architecture</h2><p>A high level overview of the Nimbus ingestion pipeline consists of the following components</p><pre><code><code>                                  ---------  Vector Fleet ----------
Load Balancer -&gt; Router Fleet -&gt; | Buffer Fleet -&gt; Aggregator Fleet |</code></code></pre><p>Traffic is fronted by an application load balancer. This is forwarded to our routing fleet which is responsible of directing customer traffic to the correct vector fleet - each customer has a separate fleet of buffers and aggregators.</p><p>Buffers are responsible for ingesting data and writing (<em>buffering</em>) it to disk before forwarding them to the aggregator. Aggregators are responsible for aggregations. All services are deployed across multiple availability zones and are orchestrated using kubernetes via <a href="https://aws.amazon.com/eks/">EKS</a></p><p>The buffer fleet is a fleet of vector instances that have both the aggregation rules as well as the aggregator endpoints of a given customer.</p><h2>Aggregations</h2><p>Aggregations are defined in YAML and are built on top of vector's <a href="https://vector.dev/docs/reference/configuration/transforms/reduce/">reduce transform</a>.</p><p>An example of an aggregation rule:</p><pre><code><code>id: cd0763c8-c4b5-4a61-aa14-51bcdee62db2
process_when: &lt;list of predicates that if true, qualify a log for aggregation&gt;
group_by: &lt;optional list of keys that aggregation will be grouped by&gt;
merge_strategies: &lt;optional key specific strategies for aggregating keys&gt;
...</code></code></pre><p>To implement stateful aggregation, we need to ensure logs belonging to an aggregation groups always get aggregated by the same aggregator. To identify logs that belong to the same aggregation group, we create a hash of their <code>id</code> and <code>group_by</code> values to uniquely identify an aggregation. We then rely on <a href="https://en.wikipedia.org/wiki/Consistent_hashing">consistent hashing</a> to route traffic to the correct aggregator. The set of aggregators is initially hardcoded doing service bootstrapping but can change due scaling events. This means we also need a mechanism of service discovery so that buffers can route traffic correctly after scaling.</p><p>Neither service discovery or consistent hashing are built in features for vector, so to make this possible, we (ab)use the <a href="https://vector.dev/docs/reference/vrl/">Vector Remap Language</a> (VRL) to carry out the logic.</p><h2>Consistent Hashing</h2><p>Consistent hashing is a specialized hashing technique that minimizes work when resizing hash slots. It works by mapping hash slot as points in a circle. When a new value is added, its hash is mapped to the circle and it is grouped to the nearest preceding slot.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XuRN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XuRN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 424w, https://substackcdn.com/image/fetch/$s_!XuRN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 848w, https://substackcdn.com/image/fetch/$s_!XuRN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 1272w, https://substackcdn.com/image/fetch/$s_!XuRN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XuRN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png" width="611" height="455" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:455,&quot;width&quot;:611,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Wikimedia image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Wikimedia image" title="Wikimedia image" srcset="https://substackcdn.com/image/fetch/$s_!XuRN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 424w, https://substackcdn.com/image/fetch/$s_!XuRN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 848w, https://substackcdn.com/image/fetch/$s_!XuRN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 1272w, https://substackcdn.com/image/fetch/$s_!XuRN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2231ec64-c14b-472f-aa80-f3e97a3272fd_611x455.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Courtesy of wikimedia, an example of assignment via consistent hashing</figcaption></figure></div><p>The following is what consistent hashing might look like for an aggregation with a single <code>group_by</code> key: <code>trace_id</code>.</p><pre><code><code>transforms:
  route/l/checkout_pre:
    type: remap
    source: |
      rule = "cd0763c8-c4b5-4a61-aa14-51bcdee62db2"
      point = abs(mod(seahash(join!([rule, to_string!(.trace_id)])), 360))
      if (point &gt;= 0 &amp;&amp; point &lt; 120) {
        .__nimroute = "agg0"
      } else if (point &gt;= 120 &amp;&amp; point &lt; 240) {
        .__nimroute = "agg1"
      } else {
        .__nimroute = "agg2"
      }
  out/l/router:
    type: route
    route:
      agg1: .__nimroute == "agg0"
      agg2: .__nimroute == "agg1"
      agg3: .__nimroute == "agg2"
    inputs: 
      - route/l/checkout_pre
</code></code></pre><p>First, the <code>join</code> function concatenates the rule name and the trace id value into a single string which is then hashed into a number using <a href="https://docs.rs/seahash/latest/seahash/">seahash</a>. We then modulo the result by 360 and take the absolute value to get to a point in the circle. The series of <code>if/else</code> statements afterwards "map" that point to the appropriate "slot", in our case, this is assigning a custom property to the log called <code>__nimroute</code>. Finally, the <code>__nimroute</code> parameter is then used by the <code>route</code> component to create different routes which will be used to route traffic to a specific aggregator.</p><h2>Service Discovery</h2><p>The Nimbus configuration service is responsible for generating vector configuration with the consistent hashing logic as well as aggregator endpoints. Because aggregators run as k8s statefulsets, they have <a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#stable-network-id">stable network ids</a>. This means that endpoint information can be constructed based on the aggregator naming scheme <code>$CUSTOMER_ID-agg-$ORDINAL.vector.svc.cluster.local</code> and the number of replicas in the stateful set.</p><p>Javascript code for getting service endpoints:</p><pre><code><code>const services = [];
for (let i=0; i &lt;REPLICAS; i++) {
  services.push(`${CUSTOMER_ID}-agg-${i}.vector.svc.cluster.local`)
}
</code></code></pre><p>The following are sinks generated for our current example with three replicas.</p><pre><code><code>sinks:
  sink/a/agg0:
    type: vector
    address: $CUSTOMER_ID-agg-0.vector.svc.cluster.local
    inputs:
      - out/l/router.agg0
  sink/a/agg2:
    type: vector
    address: $CUSTOMER_ID-agg-1.vector.svc.cluster.local
    inputs:
      - out/l/router.agg1
  sink/a/agg2:
    type: vector
    address: $CUSTOMER_ID-agg-2.vector.svc.cluster.local
    inputs:
      - out/l/router.agg2
</code></code></pre><h2>Etc</h2><p>Note that in this system, aggregators still represent a single point of failure for the set of logs that they are responsible for. In order to mitigate this, we rely on aggregators buffering data to disk (synced acrossed multiple availability zones using <a href="https://aws.amazon.com/efs/">EFS</a>) and having buffers retry sending data until aggregators come back online. This works for temporary restarts but can be problematic for longer outages due to fixed buffer sizes on the buffers. We have a secondary mechanism of also buffering data into s3 and a replay mechanism to re-send the data for the latter case. More details on this in a future post.</p><h2>Final Thoughts</h2><p>In distributed systems, managing state is often a necessary evil to deliver useful results. By combining dynamic config generation and the versatility of VRL, it is possible to implement well-known techniques such as consistent hashing and service discovery within Vector without the need for external services. Even if state can't be entirely avoided, we can design systems that minimize complexity when handling it. </p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/p/scaling-stateful-aggregations?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thank you for reading Bit by Bit (by Nimbus). This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/p/scaling-stateful-aggregations?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://bit.kevinslin.com/p/scaling-stateful-aggregations?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><p></p>]]></content:encoded></item><item><title><![CDATA[An Opinionated Guide to Managing Observability Pipelines]]></title><description><![CDATA[A step by step guide to take control of your observability data]]></description><link>https://bit.kevinslin.com/p/an-opinionated-guide-to-managing</link><guid isPermaLink="false">https://bit.kevinslin.com/p/an-opinionated-guide-to-managing</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Sun, 21 Apr 2024 13:01:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HHQf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HHQf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HHQf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HHQf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HHQf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HHQf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HHQf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg" width="1456" height="832" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Pipeline Assembly Plant&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Pipeline Assembly Plant" title="Pipeline Assembly Plant" srcset="https://substackcdn.com/image/fetch/$s_!HHQf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HHQf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HHQf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HHQf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bc603b7-04bf-4d8c-9aea-bc676bd90e8a_1792x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote></blockquote><p>Observability data, when left on its own, behaves like entropy - it increases without bounds (until you run out of <s>usable energy</s> money).</p><p>This is especially apparent this last decade with the rise of kubernetes and micro services which emit orders of magnitude more telemetry than the humble monoliths that came before it. With the increase in data volume comes the need for new tools to manage it. Enter the observability pipeline.</p><p>Observability pipelines helps organizations collect, transform, and send telemetry from any source to any destination. They are becoming increasingly popular among the enterprise because they allow organizations to control their data and limit volume before sending it upstream to vendors.</p><p>Most pipeline solutions rely on YAML for configuration. Some have a GUI that helps with generating YAML. But regardless how you do it, managing complex pipeline topologies using YAML quickly becomes error prone and hard to scale.</p><p>This post goes over a step by step example of building a scalable pipeline configuration. We'll start with a basic example of sending and receiving data and build upon it layer by layer to get to a pipeline that can handle any number of components and branching topologies.</p><h2>Vector</h2><p>For the purposes of this post, we will be writing pipeline configuration for <a href="vscode-webview://0m824acapknjiimv6p6oght39a3u0ulagbb3mj2g8cr0lmshsppo/TODO">Vector</a>, a high performance pipeline written in rust. We'll also limit the examples to working with log data. The general principals in this post will be applicable to other pipeline implementations and signals.</p><h2>1 - Send and Receive</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZCQR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZCQR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 424w, https://substackcdn.com/image/fetch/$s_!ZCQR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 848w, https://substackcdn.com/image/fetch/$s_!ZCQR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 1272w, https://substackcdn.com/image/fetch/$s_!ZCQR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZCQR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png" width="634" height="354" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/56c57095-f7be-4992-9518-9245bb139e29_634x354.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:354,&quot;width&quot;:634,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Basic Pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Basic Pipeline" title="Basic Pipeline" srcset="https://substackcdn.com/image/fetch/$s_!ZCQR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 424w, https://substackcdn.com/image/fetch/$s_!ZCQR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 848w, https://substackcdn.com/image/fetch/$s_!ZCQR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 1272w, https://substackcdn.com/image/fetch/$s_!ZCQR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56c57095-f7be-4992-9518-9245bb139e29_634x354.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is a basic pipeline configured to send and receive data. In this case, its collecting logs from the datadog agent and sending it to the datadog vendor destination.</p><pre><code><code>sources:
  source/a/dd_agent:
    type: datadog_agent
    address: 0.0.0.0:8020
    multiple_outputs: true
sinks:
  sink/l/dd:
    site: ${DD_SITE}
    type: datadog_logs
    inputs:
      - source/a/dd_agent.logs
    compression: gzip
    default_api_key: ${DD_API_KEY}

</code></code></pre><p>A few things to note about this configuration:</p><ul><li><p>vector lets you dynamically inject environmental variables to the configuration. we use this to add add secrets and configurable values such as the datadog site and api key</p></li><li><p>we're using a deliberate naming scheme for pipeline components. The convention is <code>$stage/$signal/$detailed_name</code>.</p><ul><li><p><code>$stage</code> describes the pipeline stage. a <code>source</code> receives data. a <code>sink</code> sends data.</p></li><li><p><code>$signal</code> denotes the type of data. <code>a</code> applies to all data. <code>l</code>. is logs. <code>m</code> is metrics. <code>t</code> is traces</p></li><li><p><code>$detailed_name</code> gives further detail about the <code>$stage</code> or the function of the particular component</p></li></ul></li></ul><h2>2 - Basic Pipeline</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!92iP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!92iP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 424w, https://substackcdn.com/image/fetch/$s_!92iP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 848w, https://substackcdn.com/image/fetch/$s_!92iP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 1272w, https://substackcdn.com/image/fetch/$s_!92iP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!92iP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png" width="634" height="550" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:550,&quot;width&quot;:634,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Basic Pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Basic Pipeline" title="Basic Pipeline" srcset="https://substackcdn.com/image/fetch/$s_!92iP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 424w, https://substackcdn.com/image/fetch/$s_!92iP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 848w, https://substackcdn.com/image/fetch/$s_!92iP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 1272w, https://substackcdn.com/image/fetch/$s_!92iP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F989fb153-fc42-4edc-8259-3c876ad8c9de_634x550.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The previous example was somewhat contrived since you could just send logs via an agent without using a pipeline. This example adds a transformation step to shape the data before sending it upstream.</p><pre><code><code>sources:
  source/a/dd_agent:
    type: datadog_agent
    address: 0.0.0.0:8020
    multiple_outputs: true
transforms:
  in/l/remap:
    type: remap
    inputs:
      - source/a/dd_agent.logs
    source: |
      .message = parse_json(.message) ?? .message
sinks:
  sink/l/dd:
    site: ${DD_SITE}
    type: datadog_logs
    inputs:
      - in/l/remap
    compression: gzip
    default_api_key: ${DD_API_KEY}

</code></code></pre><p>This particular transformation will try to parse the <code>message</code> field as json. If it fails, it will leave the <code>message</code> field as is. Note that this transform applies to all incoming logs and has the <code>in/</code> <code>$stage</code> prefix.</p><h2>3 - Routing and Aggregation</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3i-M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3i-M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 424w, https://substackcdn.com/image/fetch/$s_!3i-M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 848w, https://substackcdn.com/image/fetch/$s_!3i-M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 1272w, https://substackcdn.com/image/fetch/$s_!3i-M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3i-M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png" width="866" height="982" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:982,&quot;width&quot;:866,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Routing and Aggregation Pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Routing and Aggregation Pipeline" title="Routing and Aggregation Pipeline" srcset="https://substackcdn.com/image/fetch/$s_!3i-M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 424w, https://substackcdn.com/image/fetch/$s_!3i-M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 848w, https://substackcdn.com/image/fetch/$s_!3i-M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 1272w, https://substackcdn.com/image/fetch/$s_!3i-M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ddca174-0a90-4c79-b414-6f2721cc6c69_866x982.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In real world deployments, you'll tend to have more than one transformation. You'll also not want to apply all transformations to all data all the time. This example adds a second transformation that is conditionally applied via a router component.</p><pre><code><code>sources:
  source/a/dd_agent:
    type: datadog_agent
    address: 0.0.0.0:8020
    multiple_outputs: true
transforms:
  in/l/remap:
    type: remap
    inputs:
      - source/a/dd_agent.logs
    source: |
      .message = parse_json(.message) ?? .message
  in/l/route:
    type: route
    route:
      itemrefresh: (.service == "itemrefresh")
    inputs:
      - in/l/remap
  route/l/itemrefresh: 
    type: reduce
    inputs:
      - in/l/route.itemrefresh
    group_by:
      - host
    max_events: 300
sinks:
  sink/l/dd:
    site: ${DD_SITE}
    type: datadog_logs
    inputs:
      - in/l/route._unmatched
      - route/l/itemrefresh
    compression: gzip
    default_api_key: ${DD_API_KEY}
</code></code></pre><p>In this configuration, the router is forwarding logs where <code>service = itemrefresh</code> to the reduce component for aggregation. Router components have a <code>router/</code> <code>$stage</code> prefix. Non-matching logs are sent to the <code>_unmatched</code> path. All log paths are then fed into the datadog sink for sending to datadog.</p><p>Vector provides different <a href="https://vector.dev/docs/reference/configuration/transforms/reduce/#merge_strategies">merge_strategies</a> for aggregating individual message fields. In this case, we rely on the default behavior, which keeps the first string field as well as the first and last timestamp field in the aggregated output.</p><h2>4 - Multiple Routes</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dZqA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dZqA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 424w, https://substackcdn.com/image/fetch/$s_!dZqA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 848w, https://substackcdn.com/image/fetch/$s_!dZqA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 1272w, https://substackcdn.com/image/fetch/$s_!dZqA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dZqA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png" width="1456" height="970" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:970,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Multiple Routes&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Multiple Routes" title="Multiple Routes" srcset="https://substackcdn.com/image/fetch/$s_!dZqA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 424w, https://substackcdn.com/image/fetch/$s_!dZqA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 848w, https://substackcdn.com/image/fetch/$s_!dZqA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 1272w, https://substackcdn.com/image/fetch/$s_!dZqA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0bbfa00-0f71-4ea9-b72f-4888f1621bc7_1474x982.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This expands on the previous example and adds a second route that forwards logs to a different reduce component.</p><pre><code><code>sources:
  source/a/dd_agent:
    type: datadog_agent
    address: 0.0.0.0:8020
    multiple_outputs: true
transforms:
  in/l/remap:
    type: remap
    inputs:
      - source/a/dd_agent.logs
    source: |
      .message = parse_json(.message) ?? .message
  in/l/route:
    type: route
    route:
      itemrefresh: (.service == "itemrefresh")
      checkout: (.service == "checkout")
    inputs:
      - in/l/remap
  route/l/itemrefresh: 
    type: reduce
    inputs:
      - in/l/route.itemrefresh
    group_by:
      - host
    max_events: 300
  route/l/checkout: 
    type: reduce
    inputs:
      - in/l/route.checkout
    group_by:
      - customerId
      - transactionId
    max_events: 300
    merge_strategies:
      customerId: discard
      transactionId: discard
sinks:
  sink/l/dd:
    site: ${DD_SITE}
    type: datadog_logs
    inputs:
      - in/l/route._unmatched
      - route/l/*
    compression: gzip
    default_api_key: ${DD_API_KEY}

</code></code></pre><p>Vector accepts adding wildcards to route names which makes it possible for us to combine data from all routers into a single path <code>route/l/*</code>. Because each component has the signal type as part of its identifier, its easy to select components on a per signal basis.</p><h2>5 - Complex Routes</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y-uo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y-uo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 424w, https://substackcdn.com/image/fetch/$s_!Y-uo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 848w, https://substackcdn.com/image/fetch/$s_!Y-uo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 1272w, https://substackcdn.com/image/fetch/$s_!Y-uo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y-uo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png" width="1408" height="1270" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1270,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Complex Routes&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Complex Routes" title="Complex Routes" srcset="https://substackcdn.com/image/fetch/$s_!Y-uo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 424w, https://substackcdn.com/image/fetch/$s_!Y-uo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 848w, https://substackcdn.com/image/fetch/$s_!Y-uo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 1272w, https://substackcdn.com/image/fetch/$s_!Y-uo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb64ef87-61b8-407e-98a6-2c8e08bebb75_1408x1270.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In some cases, you might need to apply multiple transformations to some data. In this example, we pull up some fields nested under message to the top level. This is actually required as we use these fields in the <code>group_by</code> clause of the <a href="https://vector.dev/docs/reference/configuration/transforms/reduce/">reduce component</a> and vector does not support grouping by nested fields.</p><pre><code><code>sources:
  source/a/dd_agent:
    type: datadog_agent
    address: 0.0.0.0:8020
    multiple_outputs: true
transforms:
  in/l/remap:
    type: remap
    inputs:
      - source/a/dd_agent.logs
    source: |
      .message = parse_json(.message) ?? .message
  in/l/route:
    type: route
    route:
      itemrefresh: (.service == "itemrefresh")
      checkout: (.service == "checkout")
    inputs:
      - in/l/remap
  route/l/itemrefresh_exec: 
    type: reduce
    inputs:
      - in/l/route.itemrefresh
    group_by:
      - host
    max_events: 300
  route/l/checkout_pre: 
    type: remap
    inputs:
      - in/l/route.checkout
    source: |-
      .transactionId = .message.transactionId
      .customerId = .message.customerId
      del(.message.transactionId)
      del(.message.customerId)
  route/l/checkout/exec: 
    type: reduce
    inputs:
      - route/l/checkout_pre
    group_by:
      - customerId
      - transactionId
    max_events: 300
    merge_strategies:
      customerId: discard
      transactionId: discard
sinks:
  sink/l/dd:
    site: ${DD_SITE}
    type: datadog_logs
    inputs:
      - in/l/route._unmatched
      - route/l/*exec
    compression: gzip
    default_api_key: ${DD_API_KEY}

</code></code></pre><p>Note that we change the naming convention for components after the router. Pre-processing transformations have the <code>_pre</code> suffix appended and the main transformation has the <code>_exec</code> suffix. This lets us continue to aggregate all the routes using a single path <code>route/l/*exec</code> in the sink.</p><h2>Review</h2><p>At this point, we've built a pipeline configuration that is both flexible and easy to reason about.</p><ol><li><p>Global transformations can be applied by adding them at the beginning of the pipeline</p></li><li><p>Scoped transformations can be applied by adding them after a router component</p></li><li><p>Additional transformations can be added using suffixes before and after other transformations</p></li><li><p>All inputs and outputs are well defined with well structured names that make it easy to wire up and diagnose</p></li><li><p>Paths from any stage can be aggregated by using <code>$stage/*</code> and can be further filtered by signal <code>$stage/[l|m|t]/*</code></p></li></ol><p>The well defined naming scheme also makes it easy to do diagnostics. Vector emits the component identifier as a label on all metrics which means that it is now easy to graph related metrics in the same dashboard. The following are examples of some helpful queries written in <a href="https://prometheus.io/docs/prometheus/latest/querying/basics/">PromQL</a> syntax:</p><ul><li><p>get metric from all sources: <code>$metric_name{component_id=~"source/*"}</code></p></li><li><p>get metric from all components dealing with logs: <code>$metric_name{component_id=~".+/l/.+"}</code></p></li><li><p>get metric from all routing pre-processing components: <code>$metric_name{component_id=~".+/.+/.+_pre"}</code></p></li></ul><p>Lastly, Vector makes it easy to capture the inputs and outputs of different components and inspect data flows via <code>vector tap $component_id</code>. This command also takes wildcards which means that you can easily tap components by <code>$stage</code> and <code>$signal</code>.</p><p>An example of tapping all transformations that are scoped to a route:</p><pre><code><code>vector tap --outputs-of "route/l*"
</code></code></pre><h2>Additional Topics</h2><p>For the sake of brevity, we left out everything from this post that doesn't directly relate to managing configuration.</p><p>Other important topics:</p><ul><li><p>buffers, batches, and timeout configuration for optimizing data durability</p></li><li><p>power and perils of using custom transformations via scripting languages like <a href="https://vector.dev/docs/reference/vrl/">VRL</a> and <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/ottl/README.md">OTTL</a></p></li><li><p>high available pipeline deployment topologies</p></li><li><p>day two pipeline operations - zero downtime updates and migrations</p></li></ul><p>Look out for future posts that will dive further into these topics.</p><h2>Final Thoughts</h2><p>Pipelines are a recent but powerful addition to the modern observability stack. They help organizations limit vendor lock in and take full control over their observability data.</p><p>If you want to have the benefits of a pipeline without the toil of operations, consider <a href="https://nimbus.dev/">Nimbus</a>, a fully managed observability pipeline that analyzes your traffic and automatically optimizes your data. On average, Nimbus helps customers reduce log volume by 60% without dropping data.</p><p>At the end of the day, observability data should help you fight fires, not be the cause of internal fire fighting - if you find yourself on the wrong end of that equation, consider updating the plumbing and adopting a pipeline.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit (by Nimbus)! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Lossless Log Aggregation]]></title><description><![CDATA[Reduce Log Volume by 99% Without Dropping Data]]></description><link>https://bit.kevinslin.com/p/lossless-log-aggregation</link><guid isPermaLink="false">https://bit.kevinslin.com/p/lossless-log-aggregation</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 29 Jan 2024 15:00:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1rbJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1rbJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1rbJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1rbJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1rbJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1rbJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1rbJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Thomas Howard&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Thomas Howard" title="Thomas Howard" srcset="https://substackcdn.com/image/fetch/$s_!1rbJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!1rbJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!1rbJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!1rbJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ba690c1-91cf-4cbd-9e76-39fe8f9625d1_1024x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>On a rainy March day in 1538, Thomas Howard, the Duke of Norfolk, found himself confined within the cold stone walls of his grand estate. His fingers reluctantly penned a letter to sell his cherished lands to settle longstanding debts.</p><p>"A man can not have his cake and eat his cake", he wrote, the words etched with the pain of having to confront a reality that he did not want to accept.</p><p>This was the earliest written record of the proverb "you can't have your cake and eat it too". It's used in situations when we are forced to make tradeoffs that we'd rather not make.</p><p>When it comes to logging, that tradeoff is one of volume vs cost. You want the volume necessary to observe all your systems and you want to not spend most of your infra budget on logs. But you often can't have both.</p><h2>Not Eating Cake</h2><p>The common choice companies make when logging costs are high is to log less. It lowers the cost and also reduces the volume.</p><p>This could be deciding that you only need <code>ERROR</code> logs in production. Or deciding that certain services can do without logs at all. Or only keeping logs on the host and hoping it doesn't crash.</p><p>While this can reduce costs, it's often a temporary bandage, one with very real downsides.</p><h2>Not Having Cake</h2><p>The reason companies log in the first place is because logs are insurance. In the happy case, no one thinks about it (besides the CFO griping about the bill). In the other case, the one where things are on fire, logs are essential for engineers to properly diagnose and recover from an incident.</p><p>Incidents result in downtime and downtime impacts the business's bottom line. When Amazon went down for 40min in August 2013, a <a href="https://www.forbes.com/sites/kellyclay/2013/08/19/amazon-com-goes-down-loses-66240-per-minute/?sh=2554a3c4495c">Forbes article</a> estimated that Amazon lost $66,240 per minute during the outage</p><p>Dropping logs does decrease your costs. But you need to balance these savings with additional costs incurred by every extra second of downtime.</p><h2>Have it Both Ways</h2><p>What if you didn't need to choose? What if you could store all your logs while lowering your costs? Enter Lossless Log Aggregation (LLA).</p><p>LLA is the process of aggregating similar logs into a larger aggregate log. Common metadata and values are deduplicated and merged during the aggregation. When done effectively, this can result in a 100X reduction in volume and a 40% reduction in size. Without dropping data.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rMH8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rMH8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rMH8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rMH8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rMH8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rMH8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg" width="1456" height="568" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:568,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;LLA&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="LLA" title="LLA" srcset="https://substackcdn.com/image/fetch/$s_!rMH8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rMH8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rMH8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rMH8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F051dffdc-6a40-497c-9a36-b25de6ba7a64_2922x1140.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>Illustration of LLA</p></blockquote><h2>Types of Log Groups</h2><p>Three types of log groups are good candidates for LLA:</p><ol><li><p>Logs with common message patterns</p></li><li><p>Logs with common identifiers</p></li><li><p>Multi-line Logs</p></li></ol><p>To see before and after examples of each type, see the examples section of the <a href="https://docs.nimbus.dev/overview/examples">Nimbus Documentation</a>.</p><h3>Logs with common message patterns</h3><p>These are high-volume log events that repeat most of their content. For most applications most of the time, this will be the primary driver of log volume. Examples include health checks and heartbeat notifications.</p><h3>Logs with common identifiers</h3><p>These are logs that describe a sequence of related events. These sequences usually have some sort of common identifier like a <code>transactionId</code> or a <code>jobId</code>. Examples include a background job and business-specific user flows.</p><h3>Multi-line Logs</h3><p>These are logs where the message body can be spread across multiple new lines. Unless you add special logic on the agent side, the default behavior is to emit each newline delimited message as a separate log event.</p><h2>A Motivating Example</h2><p>Below are logs from a load balancer performing health checks on a suite of targets.</p><pre><code><code>{"timestamp":"Tue Jan 10 09:15:16 2023","host":"lb1.nimbus.dev:5678","target":"web1","path":"/health","latency":"23ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:15:18 2023","host":"lb2.nimbus.dev:5678","target":"web2","path":"/health","latency":"57ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:15:25 2023","host":"lb1.nimbus.dev:5678","target":"web4","path":"/health","latency":"14ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:15:28 2023","host":"lb2.nimbus.dev:5678","target":"web5","path":"/health","latency":"38ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:16:01 2023","host":"lb3.nimbus.dev:5678","target":"web3","path":"/health","latency":"16ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:17:16 2023","host":"lb1.nimbus.dev:5678","target":"web1","path":"/health","latency":"19ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:17:18 2023","host":"lb2.nimbus.dev:5678","target":"web2","path":"/health","latency":"41ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:17:22 2023","host":"lb3.nimbus.dev:5678","target":"web3","path":"/health","latency":"32ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:17:25 2023","host":"lb1.nimbus.dev:5678","target":"web4","path":"/health","latency":"27ms","status":"passed"}
{"timestamp":"Tue Jan 10 09:17:28 2023","host":"lb2.nimbus.dev:5678","target":"web5","path":"/health","latency":"62ms","status":"passed"}
// ... more logs
</code></code></pre><p>These are the same logs after applying LLA based on <code>host</code> and <code>status</code>:</p><pre><code><code>{"host":"lb1.nimbus.dev:5678","status":"passed","path":"/health","data":[{"target":"web1","time":"23ms"},{"target":"web4","time":"14ms"},{"target":"web1","time":"19ms"},{"target":"web4","time":"27ms"}, ...], "size":700, "timestamp":"Tue Jan 10 09:15:16 2023","timestamp_end":"Tue Jan 10 09:17:28 2023"}
{"host":"lb1.nimbus.dev:5678","status":"passed","path":"/health","data":[{"target":"web1","time":"23ms"},{"target":"web4","time":"14ms"},{"target":"web1","time":"19ms"},{"target":"web4","time":"27ms"},...], "size": 700, "timestamp":"Tue Jan 10 09:15:16 2023","timestamp_end":"Tue Jan 10 09:17:28 2023"}
{"host":"lb2.nimbus.dev:5678","status":"passed","path":"/health","data":[{"target":"web2","time":"57ms"},{"target":"web5","time":"38ms"},{"target":"web2","time":"41ms"},{"target":"web5","time":"62ms"},...], "size": 700, "timestamp":"Tue Jan 10 09:15:18 2023","timestamp_end":"Tue Jan 10 09:17:28 2023"}
{"host":"lb3.nimbus.dev:5678","status":"passed","path":"/health","data":[{"target":"web3","time":"101ms"},{"target":"web3","time":"16ms"},{"target":"web3","time":"32ms"},...],"size": 700,"timestamp":"Tue Jan 10 09:15:22 2023","timestamp_end":"Tue Jan 10 09:17:22 2023"}
// ... more logs
</code></code></pre><p>Some things to note:</p><ul><li><p>a new attribute, <code>size</code>, has been added to indicate the number of logs aggregated</p></li><li><p>a new field, <code>timestamp_end</code>, has been introduced to mark the time of the last log in the aggregation</p></li><li><p>a new field, <code>data</code>, holds an array of the unique info of each log</p></li></ul><p>For the above example, we've managed a 77% reduction in log size and a 99% reduction in log volume.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CfYO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CfYO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 424w, https://substackcdn.com/image/fetch/$s_!CfYO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 848w, https://substackcdn.com/image/fetch/$s_!CfYO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 1272w, https://substackcdn.com/image/fetch/$s_!CfYO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CfYO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png" width="1456" height="452" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:452,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Log Results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Log Results" title="Log Results" srcset="https://substackcdn.com/image/fetch/$s_!CfYO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 424w, https://substackcdn.com/image/fetch/$s_!CfYO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 848w, https://substackcdn.com/image/fetch/$s_!CfYO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 1272w, https://substackcdn.com/image/fetch/$s_!CfYO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ecf00c5-f851-4ce6-9c54-e8011be0f6b5_2454x762.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>How to do this at Home</h2><p>LLA can be implemented in an <a href="https://bit.kevinslin.com/p/first-mile-observability-and-the">observability pipeline</a> - these are data pipelines that are optimized for receiving, transforming, and sending observability data. Popular open-source pipeline solutions include the <a href="https://opentelemetry.io/docs/collector/">OTEL Collector</a> and <a href="https://vector.dev/">Vector</a>.</p><p>To configure a pipeline for LLA, you'll need to identify log groups, create forwarding rules, normalize log data before and after aggregation, as well as perform the aggregation itself over the correct fields.</p><p>You also need to pay attention to vendor-specific details - for example, Datadog has <a href="https://docs.datadoghq.com/api/latest/logs/#send-logs">limits</a> for maximum content size, array size, and the size of a single log. This means you need to be mindful of not exceeding any of these quotas when aggregating datadog logs or risk losing data.</p><p>Finally, as the pipeline itself is now part of your observability stack, you will need to operationalize it and make sure it can scale to handle traffic from all your services.</p><p>If you like the idea of LLA but not necessarily the toil, consider trying <a href="https://nimbus.dev/">Nimbus</a>. Nimbus is the first observability pipeline that automatically analyzes log traffic and can identify high-volume log groups as well as come up with LLAs to reduce their volume. On average, organizations save 60% off their logging costs within the first month of use.</p><h2>Conclusion</h2><p>It's 2024, and many companies find themselves at odds with their observability vendor - they want to ship all the logs necessary to provide a reliable service but cannot justify the expense to do so.</p><p>Like Duke Thomas, these organizations find themselves having to make the painful tradeoff of needing to part with something they need versus paying for something they cannot in good conscience afford.</p><p>But unlike in the Duke's time, there's a third choice - lossless log aggregations. Sometimes you don't have to choose, sometimes you can have your cake and eat it too!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[A Deep Dive into Observability Pricing ]]></title><description><![CDATA[Everything You Never Wanted to Know About Logging Costs]]></description><link>https://bit.kevinslin.com/p/a-deep-dive-into-observability-pricing</link><guid isPermaLink="false">https://bit.kevinslin.com/p/a-deep-dive-into-observability-pricing</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 08 Jan 2024 14:00:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xFfd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xFfd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xFfd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 424w, https://substackcdn.com/image/fetch/$s_!xFfd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 848w, https://substackcdn.com/image/fetch/$s_!xFfd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!xFfd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xFfd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png" width="1456" height="832" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3330564,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xFfd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 424w, https://substackcdn.com/image/fetch/$s_!xFfd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 848w, https://substackcdn.com/image/fetch/$s_!xFfd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!xFfd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F371eb399-a1d1-4654-8aef-8c762d3ef905_1792x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There are certain things in life that I hate doing. Taxes. Getting my teeth pulled. Calculating observability costs.</p><p>This post will be focused on the last item, calculating observability cost. Specifically, we'll focus on the usage based pricing schemes of commercial observability vendors. Since observability encompasses such a wide array of services, we'll limit the scope of this post to logging for the sake of brevity. That said, the models we&#8217;ll discuss for logging are, in broad strokes, also applicable for metrics and traces.</p><h2>Pricing Models</h2><p>When it comes to logs, there are two dominant pricing models employed by the industry:</p><ul><li><p><strong>Volume based pricing</strong>: you are charged in proportion to the total amount of data ingested, usually measured in gigabytes/month.</p></li><li><p><strong>Resource based pricing</strong>: you are charged in proportion to the amount of resources it takes to process your data, usually measured in compute consumption </p></li></ul><p>These two pricing models can be further split in terms of fixed usage vs variable usage.</p><ul><li><p><strong>Fixed cost usage</strong>: you are charged a higher per unit fee upfront but have no consumption based fee afterwards</p></li><li><p><strong>Variable cost usage</strong>: you are charged a lower per unit fee upfront but pay a variable amount based on consumption</p></li></ul><p>All together, you can plot vendors along the following two by two grid</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gJfp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gJfp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gJfp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gJfp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gJfp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gJfp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg" width="1456" height="1304" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1304,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Observability Cost Grid&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Observability Cost Grid" title="Observability Cost Grid" srcset="https://substackcdn.com/image/fetch/$s_!gJfp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gJfp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gJfp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gJfp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c637ec6-41b9-4bbe-9f9f-3c3358184d93_1902x1704.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Volume Based Pricing</h2><p>In volume based pricing, you are charged per gigabyte of log (or some gigabyte equivalent). Logs are metered at the following stages:</p><ol><li><p><strong>Ingest</strong>: This covers the ingestion of the logs as they are sent from your service to the vendor</p></li><li><p><strong>Storage</strong>: This covers the storing and indexing of logs as they are processed by the vendor</p></li><li><p><strong>Query</strong>: This covers executing search queries by users over the stored logs</p></li></ol><h3>Ingest</h3><p>The ingest stage is generally the cheapest stage of the log processing pipeline. This is because vendors want you to send them more data as this leads to lock in. Ingest to Amazon S3 is free. Ingest to datadog is $0.10/gb.</p><p>Many vendors combine ingest and storage. For example, ingest and storage in New Relic starts at $0.30/gb.</p><p>Note that there are violators of the rule of cheap ingress. Amazon Cloudwatch Logs charges $0.50/gb for ingest and $0.03/gb/month for storage.</p><h3>Storage</h3><p>The storage stage is generally where vendors make the bulk of their margin. Storage is typically measured in gb/month or per million events.</p><p>When storing log data, there is a difference between what I refer to as <strong>raw storage</strong> and <strong>indexed storage</strong>.</p><p>Raw storage is cheaper than indexed storage by an <strong>order of magnitude</strong> but usually involves variable usage costs at query time. Indexed storage is more expensive but has typically has no variable cost at query time.</p><p>Many vendors now also offer different storage tiers where you can pick what portion of your data is indexed.</p><p>Below is an updated grid with vendors divided by storage tier. Vendors at the intersection of the dotted line offer different storage tiers or have per seat pricing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hv4L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hv4L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 424w, https://substackcdn.com/image/fetch/$s_!hv4L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 848w, https://substackcdn.com/image/fetch/$s_!hv4L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!hv4L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hv4L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg" width="1456" height="1183" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1183,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Observability Cost Grid&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Observability Cost Grid" title="Observability Cost Grid" srcset="https://substackcdn.com/image/fetch/$s_!hv4L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 424w, https://substackcdn.com/image/fetch/$s_!hv4L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 848w, https://substackcdn.com/image/fetch/$s_!hv4L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!hv4L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6f48a40-758b-4e10-98d3-43ec3793076f_2092x1700.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Query</h3><p>The query stage is generally the hardest to calculate. Vendors like datadog that offer <strong>indexed storage</strong> bake the cost into their storage fee which means there is no additional query fee. </p><p>Vendors that offer <strong>raw storage</strong> charge a variable cost for searching over the data. This is usually charged by per gb scanned. This isn't an arbitrary charge - the reason that "raw" storage is cheap is because it is not indexed (which makes it cheap to store) and that extra compute needs to be expended to run queries over the data.</p><h2>Resource Based Pricing</h2><p>In resource based pricing, you are charged for the underlying processing you consume when working with logs. This is usually measured in terms of raw instances or some abstract compute unit.</p><p>In fixed cost usage, you pay for the underlying instances that are responsible for ingest, storage and query. This means you need to determine and allocate enough compute to satisfy your performance, availability, and durability concerns. This involves more work upfront but is usually cheaper, if properly provisioned, than a volume based pricing model. This is because the vendor, for volume based pricing, needs to assume the worst case (100% utilization with maximum availability and durability) while maintaining a margin - few workloads actually consume that in practice.</p><p>In variable cost usage, you pay for the underlying compute. For example, Amazon's <a href="https://aws.amazon.com/opensearch-service/features/serverless/">Serverless Opensearch Service</a> charges customers based on <a href="https://aws.amazon.com/opensearch-service/pricing/">OCUs</a> (OpenSearch Compute Units).</p><blockquote><p>One OCU comprises 6 GB of RAM, corresponding vCPU, GP3 storage (used to provide fast access to the most frequently accessed data), and data transfer to Amazon Simple Storage Service (S3)</p><p><a href="https://aws.amazon.com/opensearch-service/pricing/">Amazon OpenSearch Pricing Docs</a></p></blockquote><p>AWS will autoscale your compute depending on your usage. You tradeoff needing to manually allocate resources but end up paying a variable premium on the compute.</p><h2>Price Modifiers</h2><p>In addition to volume vs resource and fixed usage vs variable usage, there are specific options that you can buy into which multiple your costs across all pricing models.</p><p>First, there's the annual plan vs monthly plan. Annual plans lock you in for a certain usage for a year but can be 30% cheaper than the monthly plan. While a 30% discount seems great, effective use of an annual plan involves being able to effectively forecast your log usage across all the factors we've so far discussed. I have yet to see a company at any scale be able to do this effectively. </p><p>There's also enterprise discounts at higher volumes. These discounts can be significant but like the annual plan, require hefty upfront commitments.</p><p>Second, there's retention - the number of days a vendor will keep your logs indexed. This can be as short as a day to as long as indefinite. The higher the retention, the more it will cost to store the logs.</p><p>Finally, there's "value add" options that vendors provide like "sensitive data scanning" and extra compliance guarantees. These options can double the price per gigabyte during ingestion and storage.</p><h2>Pricing Comparison</h2><p>At this point, you might have an inkling of why observability pricing is hard to calculate, never mind compare. No two vendors have the same pricing model and they each charge for a different permutation of usage.</p><p>Nevertheless, we're going to do a best effort attempt by making a number of assumptions to normalize costs across vendors.</p><h3>Volume Based Fixed Cost Comparison</h3><p>The following is a comparison of just the volume based pricing of different vendors in terms of cost per gigabyte per month. It does not cover vendors that use resource based pricing and does not take into account the variable based portion of the cost.</p><p>We make the following assumptions when calculating costs:</p><ul><li><p>for vendors that have a fixed price component ($X for first Y GB and then $Z/gb/month afterwards) - we are ignoring this as it an insignificant part of the cost at scale</p></li><li><p>for vendors that have event based pricing ($X per million events), we're assuming that the average event is 512 bytes and normalizing to gb/month</p></li><li><p>for vendors that have indexed storage with variable retention prices, we normalize to 14day retention</p></li><li><p>for vendors that have multiple tiers of storage (eg. hot and cold), we compare prices for "hot" storage</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2S3P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2S3P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2S3P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2S3P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2S3P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2S3P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg" width="1360" height="698" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:698,&quot;width&quot;:1360,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Pivot Table&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Pivot Table" title="Pivot Table" srcset="https://substackcdn.com/image/fetch/$s_!2S3P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2S3P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2S3P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2S3P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03c01280-db71-4c18-bc50-903c5ed634af_1360x698.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Some observations:</p><ul><li><p>indexed storage vendors have the highest price per byte and raw storage vendors have the lowest.</p></li><li><p>there is a 33X difference between the most expensive vendor (Datadog at $5.08/gb/mo) and the least (Axiom at $0.15/gb/mo)</p></li><li><p>the new generation of vendors that have started within the last 10 years (eg. axiom, signoz, hyperdx, etc. ) have drastically lower volume based prices ($0.50/gb/mo or lower)</p></li></ul><h3>Volume Based Variable Cost Comparison</h3><p>Only looking at fixed cost can paint a distorted picture of the vendor landscape - variable costs can easily exceed the fixed cost depending on the workload.</p><p>We make the following assumptions when calculating costs:</p><ul><li><p>variable usage per month is <code>total team size * volume of data ingested * 10</code> (this makes the assumption that each team member, on average, queries 10X the monthly ingest volume each month)</p></li><li><p>S3 query cost is modeled as the cost of scanning data using Athena ($0.005/gb scanned)</p></li></ul><p>The following is a chart of vendors that have a variable cost during query time. We model a team size of 10 with a monthly data volume of 1TB.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AwGE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AwGE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 424w, https://substackcdn.com/image/fetch/$s_!AwGE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 848w, https://substackcdn.com/image/fetch/$s_!AwGE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 1272w, https://substackcdn.com/image/fetch/$s_!AwGE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AwGE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png" width="1190" height="734" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:734,&quot;width&quot;:1190,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Query based pricing&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Query based pricing" title="Query based pricing" srcset="https://substackcdn.com/image/fetch/$s_!AwGE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 424w, https://substackcdn.com/image/fetch/$s_!AwGE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 848w, https://substackcdn.com/image/fetch/$s_!AwGE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 1272w, https://substackcdn.com/image/fetch/$s_!AwGE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F934dd5ad-0429-4dee-87d6-f7bf4f9ab028_1190x734.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Some observations:</p><ul><li><p>Cloudwatch Logs cost doubles as the variable cost just about equals the volume cost</p></li><li><p>Grafana Cloud has a <a href="https://grafana.com/docs/grafana-cloud/cost-management-and-billing/understand-your-invoice/logs-invoice/?pg=pricing&amp;plcmt=logs-details#fair-use-policy-for-gbs-queried">fair use policy</a> that makes it free to query 100X the monthly volume - this is why you don't see a variable cost component for Grafana at 10 people. Note that if exceeded, Grafana becomes extremely expensive at query time ($0.40/gb scanned)</p></li><li><p>New Relic charges per seat - at $418.80/full platform user, the total price increases by over 13X</p></li><li><p>S3 query cost becomes the bulk of any logging service built on top of it</p></li></ul><h3>Volume Based Combined Cost Comparison</h3><p>The following two charts attempt to show the total cost of ownership when accounting for both the fixed and variable portions of volume based pricing when processing 1TB of logs per month for a team of 10.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ti6d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ti6d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ti6d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ti6d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ti6d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ti6d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg" width="1190" height="736" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:736,&quot;width&quot;:1190,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Combined 1TB Cost&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Combined 1TB Cost" title="Combined 1TB Cost" srcset="https://substackcdn.com/image/fetch/$s_!ti6d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ti6d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ti6d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ti6d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa63efffd-3cc9-4535-b6a6-f1597c071def_1190x736.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Some observations:</p><ul><li><p>New Relic has the biggest jump in pricing due to their per seat model</p></li><li><p>Cloudwatch logs and S3 become drastically more expensive when taking into account querying</p></li></ul><p>Note that there are many "raw storage" vendors that do not (currently) have a variable based cost associated with querying (eg. Highlight, HypderDX, Signoz, BetterStack). These vendors are all built on top of either S3 (or a cloud vendor equivalent) or Clickhouse (which leverages S3).</p><p>This is most likely due to a combination of the following:</p><ul><li><p>our estimate of queries scaling at 10X monthly ingest volume per user is far too conservative</p></li><li><p>the naive assumption of using S3 + Athena to model S3 query is far too conservative</p></li><li><p>the aforementioned vendors are able to leverage caching, pre-computation, and other optimizations to drastically reduce the cost of serving queries</p></li><li><p>the aforementioned vendors are all "new" to the scene (started within the last 5 years) and don't have customers of the scale and workload that break their current pricing model</p></li></ul><h2>Conclusion</h2><p>Pricing is tricky. Usage based pricing means you pay little when you're small but that your costs can grow exponentially if you scale. This makes it hard to compare or forecast.</p><p>If you're just starting out, it probably doesn't matter because costs are minimal and you will get more leverage by focusing on your business. If you're a growing or mature organization and you realize that observability is over 20% of your infra budget or past the seven figure range, it might be worthwhile to look into optimizations (eg. <a href="https://blog.pragmaticengineer.com/datadog-65m-year-customer-mystery/">see Datadog's $65M/year customer</a>)</p><p>The kicker is that by the <strong>time you want to optimize</strong> is also when you're <strong>most locked in</strong>. Existing instrumentation, training, run books, dashboards, alarms - all this and more needs to be migrated at scale without breaking existing services or slowing down product velocity. To switch might mean dedicating a <a href="https://aws.amazon.com/executive-insights/content/amazon-two-pizza-team/">two pizza team</a> for an entire year to deliver an outcome that in all likelihood will be worse than what you have in place today. This is why its worth understanding not just what you're costs are today but how they will scale when you grow. </p><p>If you're already in too deep or you just want someone to make the existing costs go away - check out <a href="https://www.nimbus.dev/">Nimbus</a> (disclaimer: I'm the founder). The Nimbus platform helps companies reduce logging costs by 60% or more with no migrations or code changes. Our optimization engine automatically analyzes incoming logs (metrics and traces available in limited preview) to identify common patterns and create transformations that reduce log volumes without dropping data. If controlling observability costs is a concern, also feel free to reach out directly to kevin(at)nimbus.dev</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Logging with OpenTelemetry and Loki]]></title><description><![CDATA[A step-by-step guide to using OpenTelemetry's Logging SDK with Grafana Loki and NodeJS]]></description><link>https://bit.kevinslin.com/p/logging-with-opentelemetry-and-loki</link><guid isPermaLink="false">https://bit.kevinslin.com/p/logging-with-opentelemetry-and-loki</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 11 Sep 2023 14:01:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sxfw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sxfw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sxfw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!sxfw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!sxfw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!sxfw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sxfw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki with logs&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki with logs" title="Loki with logs" srcset="https://substackcdn.com/image/fetch/$s_!sxfw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!sxfw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!sxfw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!sxfw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fad7f1804-1416-46f6-819c-e911ead4d4cb_1024x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>Abstract of Loki warmings himself with logs, digital art</p></blockquote><p>OpenTelemetry (OTEL) is the industry standard for managing telemetry data. The working group's main focus for the past three years is supporting the <a href="https://www.datadoghq.com/three-pillars-of-observability/">three pillars</a> of observability: logs, metrics, and traces.</p><p>Today, there is a growing number of organizations that use OTEL for traces and metrics. It is not so for logging. In large part, this is because the logging API was not marked stable until <a href="https://github.com/open-telemetry/opentelemetry-specification/pull/3376">April this year</a>. Most people I talk to are still under the impression that OTEL does not support logs.</p><p>Today's post will attempt to correct the misconception. We will be using the <a href="https://opentelemetry.io/docs/instrumentation/js/">NodeJS OTEL SDK</a> to add logging to a simple web app. Logs will be collected using the otel agent (<a href="https://github.com/open-telemetry/opentelemetry-collector-contrib">otel-collector-contrib</a>) and exported to <a href="https://grafana.com/oss/loki/">Loki</a>, an open-source logging backend. Traces will be generated via auto-instrumentation and auto-correlated with logs (and vice versa). <a href="https://opentelemetry.io/docs/concepts/semantic-conventions/">Semantic conventions</a> will be mentioned.</p><p>For those who want to jump directly to the code, you can download the result on <a href="https://github.com/nimbushq/otel-node-demo">Github</a>.</p><h2>Setup the App</h2><p>First, initialize your nodejs project and install <a href="https://expressjs.com/">express</a> (a minimalist web framework for nodejs).</p><pre><code><code>mkdir otel-node-demo
cd otel-node-demo
npm init -y
npm i express
</code></code></pre><p>Next, write the application code. This application exposes a single endpoint, <code>/rolldice</code> , which will return a random number between 1 and 6.</p><ul><li><p><code>app.js</code></p></li></ul><pre><code><code>const express = require('express');
const PORT = parseInt(process.env.PORT || '8080');
const app = express();

app.get('/rolldice', (req, res) =&gt; {
  const randNum = getRandomNumber(1, 6)
  res.send((randNum).toString() + "\n");
});

app.listen(PORT, () =&gt; {
  console.log(`Listening for requests on http://localhost:${PORT}`);
});
</code></code></pre><p>Start the application. This will run an express server listening on port <code>8080</code></p><pre><code><code>$ node app.js
Listening for requests on http://localhost:8080
</code></code></pre><p>In a separate terminal, you can roll the dice by sending a few requests to the <code>/rolldice</code> endpoint</p><pre><code><code>$ curl localhost:8080/rolldice
5
$ curl localhost:8080/rolldice
3
</code></code></pre><h2>Add Logs</h2><p>To add logs, start by installing the necessary OTEL dependencies</p><pre><code><code>npm i @opentelemetry/sdk-node \
  @opentelemetry/api \
  @opentelemetry/sdk-logs 
</code></code></pre><blockquote><p>NOTE: The status of the Javascript Logging SDK is currently in <a href="https://opentelemetry.io/docs/instrumentation/js/">development</a></p></blockquote><p>Next, create an initialization file for the OTEL <code>NodeSDK</code></p><ul><li><p><code>instrumentation.js</code></p></li></ul><pre><code><code>const { NodeSDK } = require('@opentelemetry/sdk-node');
const {
  SimpleLogRecordProcessor,
  ConsoleLogRecordExporter,
} = require('@opentelemetry/sdk-logs');

const sdk = new NodeSDK({
  logRecordProcessor: new SimpleLogRecordProcessor(
    // writes logs to stdout
    new ConsoleLogRecordExporter()
),
});
sdk.start();
</code></code></pre><p>Our current configuration will write logs to <code>STDOUT</code>. To write logs from our application, we need to acquire a logger instance and emit a log.</p><ul><li><p>app.js</p></li></ul><pre><code><code>diff --git a/app.js b/app.js
index 8ee1efe..b916e9a 100644
--- a/app.js
+++ b/app.js
@@ -1,6 +1,8 @@
+const { logs, SeverityNumber } = require('@opentelemetry/api-logs');
+const logger = logs.getLogger('app', '0.1.0');
 
 app.get('/rolldice', (req, res) =&gt; {
   const randNum = getRandomNumber(1, 6)
+  const attributes = {
+    'roll': randNum
+  }
+  logger.emit({
+    severityNumber: SeverityNumber.INFO,
+    body: "exiting /rolldice",
+    attributes
+  });
   res.send(randNum.toString() + "\n");
 });
</code></code></pre><p>We can now restart our application with the instrumentation code preloaded.</p><pre><code><code># cancel the running server 
$ CTRL-C

# restart the server while preloading our custom instrumentation
$ node -r ./instrumentation.js app.js
</code></code></pre><blockquote><p>NOTE: The reason we wrote the SDK initialization code in a separate file is to set up instrumentation before loading the main script. This is not required for logging but it is for auto-instrumenting tracing (something we'll cover later in this post).</p></blockquote><p>Sending a request should now yield JSON from the server</p><pre><code><code>$ curl localhost:8080/rolldice
</code></code></pre><p>Output from the server shell</p><pre><code><code>{
  timestamp: 1694367487002000,
  traceId: undefined,
  spanId: undefined,
  traceFlags: undefined,
  severityText: undefined,
  severityNumber: 9,
  body: 'exiting /rolldice',
  attributes: { roll: 1 }
}
</code></code></pre><blockquote><p>NOTE: There are lots of fields like <code>traceId</code> and <code>spanId</code> that are currently <code>undefined</code> - these will be automatically filled when we add tracing.</p></blockquote><h2>Initialize the Collector</h2><p>To send telemetry data, we need an endpoint that can ingest the OpenTelemetry Protocol (OLTP). For this guide, we will be using <code>otel-collector-contrib</code></p><h3>Download the collector</h3><p>You can find the latest releases of the collector for all architectures <a href="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/tag/v0.84.0">here</a>. In my case, since I'm running on an M1 Macbook, I will be using the <a href="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.84.0/otelcol-contrib_0.84.0_darwin_arm64.tar.gz">otelcol-contrib_0.84.0_darwin_arm64.tar.gz</a></p><p>After downloading, unpack and verify that the collector is working.</p><pre><code><code>$ cd ~/Downloads
$ tar -xzvf otelcol-contrib_0.84.0_darwin_arm64.tar.gz
$ ./otelcol-contrib -v

otelcol-contrib version 0.84.0
</code></code></pre><blockquote><p>NOTE: If you are running on a Mac, you might get the following warning. &gt; The latest Mac OS checks will warn you whenever you try to execute software that has not explicitly gone through Apple's review process (most open-source software falls into this camp). You can bypass this warning by following the instructions <a href="https://www.easeus.com/computer-instruction/apple-cannot-check-it-for-malicious-software.html#Fix_2">here</a></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jtdG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jtdG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jtdG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jtdG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jtdG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jtdG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg" width="486" height="548" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:548,&quot;width&quot;:486,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Mac OTEL system warning&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Mac OTEL system warning" title="Mac OTEL system warning" srcset="https://substackcdn.com/image/fetch/$s_!jtdG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jtdG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jtdG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jtdG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ad709-d4bc-4850-8cf5-38dcc17d412d_486x548.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Create a collector config</h3><p>The OTEL configuration tells the collector how to manage logs. In our case, we want to receive logs via OLTP and export it to Loki using the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/lokiexporter/README.md">lokiexporter</a></p><ul><li><p><code>otel.yaml</code></p></li></ul><pre><code><code>receivers:
  otlp/app:
    protocols:
      grpc:
      http:
exporters:
  loki:
    endpoint: "${env:LOGS_ENDPOINT}"
    auth:
      authenticator: basicauth/logs
extensions:
  basicauth/logs:
    client_auth:
      username: "${env:LOGS_USERNAME}"
      password: "${env:LOGS_PWD}"
service:
  extensions: [basicauth/logs]
  pipelines:
    logs/gc:
      receivers:
        - otlp/app
      exporters: [loki]
</code></code></pre><p>Note that we use the <code>basicauth</code> extension to authenticate access to our Loki instance. The OTEL collector can make use of environment variables inside the config using the <code>${env:KEY}</code> syntax.</p><h3>Get Loki configuration details</h3><blockquote><p>NOTE: The following is written with <a href="https://grafana.com/">Grafana Cloud</a> in mind and the Loki instance that comes with the free account. If you are self-hosting, the same configuration still applies but your authentication details will differ based on your setup.</p></blockquote><p>You can find the config values for your endpoints by going to <code>https://grafana.com/orgs/{YOUR_ORG}</code> and clicking <code>Details</code> on the Loki data source. You might need to create an authentication token if one does not already exist. You can find further instructions for authenticating with grafana cloud <a href="https://grafana.com/docs/opentelemetry/collector/send-otlp-to-grafana-cloud-databases/">here</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WoLp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WoLp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 424w, https://substackcdn.com/image/fetch/$s_!WoLp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 848w, https://substackcdn.com/image/fetch/$s_!WoLp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!WoLp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WoLp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg" width="1456" height="1163" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1163,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Grafana Details&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Grafana Details" title="Grafana Details" srcset="https://substackcdn.com/image/fetch/$s_!WoLp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 424w, https://substackcdn.com/image/fetch/$s_!WoLp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 848w, https://substackcdn.com/image/fetch/$s_!WoLp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!WoLp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9598fd78-2df5-4425-abb3-50d82fe2d54e_2120x1694.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Start the collector</h3><p>For our demo app, we are going to use <a href="https://thenewstack.io/what-is-the-docker-env-file-and-how-do-you-use-it">env files</a> and <a href="https://www.npmjs.com/package/dotenv">dotenv</a> to simplify passing configuration details to the collector.</p><p>First, install dotenv and create a <code>.env</code> file</p><pre><code><code>npm i -g dotenv
# make sure to not commit your .env file
echo ".env" &gt;&gt; .gitignore
touch .env
</code></code></pre><p>Fill the .env file with the relevant configuration values</p><pre><code><code>LOGS_ENDPOINT=
LOGS_USERNAME=
LOGS_PWD=
</code></code></pre><p>Start the collector in a separate terminal</p><pre><code><code># to simplify instructions, we move the collector to the same directory as our application
$ mv /path/to/otelcol-contrib .

$ npx dotenv -- ./otelcol-contrib --config otel.yaml

2023-09-10T11:15:48.834-0700    info    service/telemetry.go:84 Setting up own telemetry...
...
2023-09-10T11:15:48.835-0700    info    service/service.go:161  Everything is ready. Begin running and processing data.
</code></code></pre><p>At this point, you should have three terminals carrying out the following functions:</p><ul><li><p>running your application</p></li><li><p>running the collector</p></li><li><p>making curl requests to the application</p></li></ul><h2>Update the App to Export Logs</h2><p>We now need to update our app to send logs to the OLTP endpoint (instead of <code>STDOUT</code>).</p><p>First, add the required OTEL package to export logs</p><pre><code><code>npm i @opentelemetry/exporter-logs-otlp-proto
</code></code></pre><p>Next, update the SDK initialization logic to use the <code>OLTPLogsExporter</code></p><ul><li><p>instrumentation.js</p></li></ul><pre><code><code>diff --git a/instrumentation.js b/instrumentation.js
index 3f76659..663129d 100644
--- a/instrumentation.js
+++ b/instrumentation.js
@@ -2,10 +2,12 @@
 const { NodeSDK } = require('@opentelemetry/sdk-node');
 const {
   SimpleLogRecordProcessor,
-  ConsoleLogRecordExporter,
 } = require('@opentelemetry/sdk-logs');
+const {
+  OTLPLogsExporter
+} = require('@opentelemetry/exporter-logs-otlp-proto');
 
 const sdk = new NodeSDK({
-  logRecordProcessor: new SimpleLogRecordProcessor(new ConsoleLogRecordExporter()),
+  logRecordProcessor: new SimpleLogRecordProcessor(new OTLPLogsExporter()),
 });
 sdk.start();
</code></code></pre><p>Finally, restart the app</p><pre><code><code># cancel the running server 
$ CTRL-C 

# restart
$ node -r ./instrumentation.js app.js
</code></code></pre><p>Now send some requests to the app endpoint. You'll notice that logs no longer show up in the console.</p><h2>Query for Logs</h2><p>If everything is instrumented correctly, logs should now be appearing in Loki. To find them, login to Grafana and go to the <code>Explore</code> panel of the Grafana console</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FvQj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FvQj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 424w, https://substackcdn.com/image/fetch/$s_!FvQj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 848w, https://substackcdn.com/image/fetch/$s_!FvQj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!FvQj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FvQj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg" width="1456" height="1059" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1059,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Grafana Explore&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Grafana Explore" title="Grafana Explore" srcset="https://substackcdn.com/image/fetch/$s_!FvQj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 424w, https://substackcdn.com/image/fetch/$s_!FvQj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 848w, https://substackcdn.com/image/fetch/$s_!FvQj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!FvQj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4de94bc-0fbe-4a58-a806-1e04b0dc68b8_2851x2074.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Choose the Loki data source</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CDQ0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CDQ0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 424w, https://substackcdn.com/image/fetch/$s_!CDQ0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 848w, https://substackcdn.com/image/fetch/$s_!CDQ0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!CDQ0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CDQ0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg" width="1456" height="1049" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1049,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Grafana data source&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Grafana data source" title="Grafana data source" srcset="https://substackcdn.com/image/fetch/$s_!CDQ0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 424w, https://substackcdn.com/image/fetch/$s_!CDQ0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 848w, https://substackcdn.com/image/fetch/$s_!CDQ0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!CDQ0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F470ec7a0-4f3a-483b-8e62-93d4d78df25b_2858x2060.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Run the following query</p><pre><code><code>{exporter="OTLP"}
</code></code></pre><blockquote><p>NOTE: The search syntax we used is <a href="https://grafana.com/docs/loki/latest/query/">LogQL</a>. It is similar to <a href="https://prometheus.io/docs/prometheus/latest/querying/basics/">PromQL</a> (for good and bad...) and lets you search logs in Loki.</p></blockquote><p>You should get the following output</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mPYo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mPYo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mPYo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mPYo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mPYo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mPYo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg" width="1456" height="1054" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1054,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki Logs&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki Logs" title="Loki Logs" srcset="https://substackcdn.com/image/fetch/$s_!mPYo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mPYo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mPYo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mPYo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3acf62b-f315-4446-8a36-5fc7c258da73_2900x2099.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <code>lokiexporter</code> automatically attaches three labels by default to all outgoing logs: <code>exporter</code> (always set to OTLP), <code>job</code> (set to <code>service.name</code>), and <code>level</code> (log level).</p><blockquote><p>NOTE: Loki differs from traditional solutions like <code>elasticsearch</code> in that it only indexes labels instead of the entire log line.</p></blockquote><p>Our initial query tells Loki to fetch all logs that have the <code>exporter=OLTP</code> label. By default, the <code>lokiexporter</code> does not turn any other fields into labels.</p><h2>Add Labels</h2><p>If you inspect the JSON payload, you'll notice that the log message has <code>attributes</code> and <code>resources</code></p><pre><code><code>{
  "body": "exiting /rolldice",
  "attributes": {
    "roll": 3
  },
  "resources": {
    ...
    "service.name": "unknown_service:node",
    "telemetry.sdk.language": "nodejs",
  },
  ...
}
</code></code></pre><p>In OTEL, a <a href="https://opentelemetry.io/docs/concepts/glossary/#resource">resource</a> is metadata about the entity producing telemetry (eg. the service name). An <a href="https://opentelemetry.io/docs/concepts/glossary/#attribute">attribute</a> is all other metadata (eg. the roll).</p><p>The <code>lokiexporter</code> looks for the special attribute key <code>loki.attribute.labels</code> to determine whether additional attributes should be converted to labels. The resource attribute key <code>loki.resource.labels</code> serves the same purpose for resources.</p><p>You can add additional metadata to the log payload to tell Loki to treat certain fields as labels. While we could do this in code, it's much more efficient to do this as a <code>processor</code> in the collector. This allows us to automatically add the metadata to all log telemetry without changing the code.</p><p>To add custom attributes in the collector, use the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/attributesprocessor/README.md">attribute</a> processor. Use the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourceprocessor">resource</a> processor to do the same for resource metadata. We will now use these processors to add <code>roll</code> and <code>service.name</code> as Loki labels.</p><pre><code><code>diff --git a/otel.yaml b/otel.yaml
index badf962..e74378c 100644
--- a/otel.yaml
+++ b/otel.yaml
@@ -3,6 +3,17 @@ receivers:
     protocols:
       grpc:
       http:
+processors:
+  resource/loki:
+    attributes:
+      - action: insert
+        key: loki.resource.labels
+        value: service.name
+  attributes/loki:
+    actions:
+      - action: insert
+        key: loki.attribute.labels
+        value: roll
@@ -19,4 +30,5 @@ service:
  pipelines:
     logs/gc:
       receivers:
         - otlp/app
+      processors: [resource/loki, attributes/loki]
       exporters: [loki]
</code></code></pre><p>Now restart the collector. Like restarting the server, this involves sending a terminating signal to the collector and restarting</p><pre><code><code># in the terminal where the collector is running
CTRL-C

# restart the collector
$ npx dotenv -- ./otelcol-contrib --config otel.yaml
</code></code></pre><blockquote><p>NOTE: Since all the changes are on the collector side, we do not need to restart the application or change the application code for the changes to take effect.</p></blockquote><p>Now send a few more requests to the application. Query for logs in Loki again and you should see the following result:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WSf1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WSf1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 424w, https://substackcdn.com/image/fetch/$s_!WSf1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 848w, https://substackcdn.com/image/fetch/$s_!WSf1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!WSf1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WSf1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg" width="1456" height="373" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:373,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;New Log results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="New Log results" title="New Log results" srcset="https://substackcdn.com/image/fetch/$s_!WSf1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 424w, https://substackcdn.com/image/fetch/$s_!WSf1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 848w, https://substackcdn.com/image/fetch/$s_!WSf1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!WSf1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73901d37-405e-482e-bc55-518a92111c5d_2842x728.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><code>roll</code> and <code>service.name</code> are now both labels.</p><blockquote><p>NOTE: Loki does not allow for <code>.</code> in the label name - the <code>lokiexporter</code> automatically translates <code>.</code> into <code>_</code></p></blockquote><p>You can now directly query the new labels. For example, querying by <code>roll</code> value</p><pre><code><code>{roll="4"}
</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iCYU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iCYU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iCYU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iCYU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iCYU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iCYU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg" width="1456" height="834" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:834,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki Query Results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki Query Results" title="Loki Query Results" srcset="https://substackcdn.com/image/fetch/$s_!iCYU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 424w, https://substackcdn.com/image/fetch/$s_!iCYU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 848w, https://substackcdn.com/image/fetch/$s_!iCYU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!iCYU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99bf6f23-6a6c-4b59-bff9-6fbe614ff49b_2880x1650.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Use Semantic Conventions</h2><p>If you look closely at the JSON log payload, you might have noticed that the service name is currently not helpful: <code>unknown_service:node</code>. This is because we have not yet set the name in our application.</p><p>The service name can be set when the SDK is initialized. It serves as the primary identifier of the service being instrumented. The service name key and its description are specified by the OTEL <a href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/README.md">Semantic Convetions</a>. These are a set of common values used to describe metadata associated with telemetry.</p><p>You can access semantic convention keys by using the language-specific helper packages.</p><pre><code><code>npm i @opentelemetry/semantic-conventions @opentelemetry/resources 
</code></code></pre><ul><li><p>instrumentation.js</p></li></ul><pre><code><code>diff --git a/instrumentation.js b/instrumentation.js
index 663129d..4e2d651 100644
--- a/instrumentation.js
+++ b/instrumentation.js
@@ -6,8 +6,19 @@ const {
 const {
   OTLPLogsExporter
 } = require('@opentelemetry/exporter-logs-otlp-proto');
+const { Resource } = require('@opentelemetry/resources');
+const {
+  SemanticResourceAttributes,
+} = require('@opentelemetry/semantic-conventions');
 
+const resource = Resource.default().merge(
+  new Resource({
+    [SemanticResourceAttributes.SERVICE_NAME]: "otel-node-demo",
+    [SemanticResourceAttributes.SERVICE_VERSION]: "0.1.0",
+  }),
+);
 const sdk = new NodeSDK({
+  resource,
   logRecordProcessor: new SimpleLogRecordProcessor(new OTLPLogsExporter()),
 });
 sdk.start();
</code></code></pre><p>Since we want to index the service version, we are also going to update our collector configuration to convert it to a Loki label.</p><ul><li><p>otel.yaml</p></li></ul><pre><code><code>diff --git a/otel.yaml b/otel.yaml
index badf962..e74378c 100644
--- a/otel.yaml
+++ b/otel.yaml
processors:
  resource/loki:
    attributes:
      - action: insert
        key: loki.resource.labels
-        value: service.name
+        value: service.name, service.version
</code></code></pre><p>Because we've changed both the collector and the instrumentation code, we need to restart both components for the changes to take effect.</p><p>After restarting and sending a few requests, query again in Loki to see both the service name and version show up in the results</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3efq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3efq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3efq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3efq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3efq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3efq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg" width="1456" height="476" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:476,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki Results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki Results" title="Loki Results" srcset="https://substackcdn.com/image/fetch/$s_!3efq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 424w, https://substackcdn.com/image/fetch/$s_!3efq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 848w, https://substackcdn.com/image/fetch/$s_!3efq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!3efq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58318443-ba68-4708-8de1-38a7679f7b08_2810x918.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Add Auto Instrumentation</h2><p>By adding OTEL traces to our application, we get trace information in our logs for free.</p><p>First, install the auto instrumentation package. This will automatically inject traces into your application at run time.</p><pre><code><code>npm i @opentelemetry/auto-instrumentations-node
</code></code></pre><p>Next, initialize the OTEL SDK with the auto instrumentations.</p><ul><li><p>instrumentation.js</p></li></ul><pre><code><code>diff --git a/instrumentation.js b/instrumentation.js
index 4e2d651..6a0f794 100644
--- a/instrumentation.js
+++ b/instrumentation.js
@@ -10,15 +10,19 @@ const { Resource } = require('@opentelemetry/resources');
 const {
   SemanticResourceAttributes,
 } = require('@opentelemetry/semantic-conventions');
+const {
+  getNodeAutoInstrumentations,
+} = require('@opentelemetry/auto-instrumentations-node');
 
 const resource = Resource.default().merge(
   new Resource({
     [SemanticResourceAttributes.SERVICE_NAME]: "otel-node-demo",
-    [SemanticResourceAttributes.SERVICE_VERSION]: "0.1.0",
+    [SemanticResourceAttributes.SERVICE_VERSION]: "0.1.1",
   }),
 );
 const sdk = new NodeSDK({
   resource,
   logRecordProcessor: new SimpleLogRecordProcessor(new OTLPLogsExporter()),
+  instrumentations: [getNodeAutoInstrumentations()],
 });
 sdk.start();
</code></code></pre><p>Now, restart the application, make a few requests, and run a new query in Loki.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XvzX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XvzX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 424w, https://substackcdn.com/image/fetch/$s_!XvzX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 848w, https://substackcdn.com/image/fetch/$s_!XvzX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!XvzX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XvzX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg" width="1456" height="531" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:531,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki Results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki Results" title="Loki Results" srcset="https://substackcdn.com/image/fetch/$s_!XvzX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 424w, https://substackcdn.com/image/fetch/$s_!XvzX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 848w, https://substackcdn.com/image/fetch/$s_!XvzX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!XvzX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa70fef7b-c165-4917-be53-e3204f9cf35b_2842x1036.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You should see the following fields in the log payload. These values were added automatically by the trace.</p><pre><code><code>{
  "body": "exiting /rolldice",
  "traceid": "b94fec1be8a7c4ac52b1d633b18afc17",
  "spanid": "acf2dd52184b791e",
  ...
}
</code></code></pre><p>Even though we're not using traces in our app, having the <code>traceid</code> and <code>spanid</code> as part of the log metadata makes the logs more useful as you can query those ids to get what are essentially trace events.</p><p>If we took the extra step of actually exporting traces, we would be able to jump directly to the trace from the log (aka the big blue button from the diagram below). Unfortunately, we won't get any results right now since we haven't done that.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X-NL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X-NL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X-NL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X-NL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X-NL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!X-NL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg" width="1456" height="855" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:855,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki Results&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki Results" title="Loki Results" srcset="https://substackcdn.com/image/fetch/$s_!X-NL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X-NL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X-NL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X-NL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c11e41e-63c9-4eec-9e1d-f58cb716dd20_3394x1992.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Export Traces</h2><p>Let's export traces to see what results would look like. First, install the tracing exporter package.</p><pre><code><code>npm i @opentelemetry/exporter-trace-otlp-proto
</code></code></pre><p>Next, update the OTEL configuration to send traces to <a href="https://grafana.com/oss/tempo/">Tempo</a>. Tempo is an open-source distributed tracing backend that is also available in Grafana Cloud. Don't forget to also update your configuration in <code>.env</code> to pass in the <code>TRACE_*</code> configuration.</p><ul><li><p>otel.yaml</p></li></ul><pre><code><code>diff --git a/otel.yaml b/otel.yaml
index 167a6b2..5ac7201 100644
--- a/otel.yaml
+++ b/otel.yaml
  exporters:
+  otlp/traces:
+    endpoint: ${env:TRACES_ENDPOINT}
+    tls:
+      insecure: false
+    auth:
+      authenticator: basicauth/traces
 extensions:
+  basicauth/traces:
+    client_auth:
+      username: "${env:TRACES_USERNAME}"
+      password: "${env:TRACES_PWD}"
 service:
   extensions: [basicauth/logs]
   pipelines:
+    traces/gc:
+      receivers:
+        - otlp/app
+      exporters: [otlp/traces]
</code></code></pre><blockquote><p>NOTE: Tempo has built-in OTLP support and thus works with the builtin OLTP exporter</p></blockquote><p>Finally, update the app to export traces</p><ul><li><p>app.js</p></li></ul><pre><code><code>diff --git a/instrumentation.js b/instrumentation.js
index 6a0f794..2265a4d 100644
--- a/instrumentation.js
+++ b/instrumentation.js
@@ -13,16 +13,20 @@ const {
const {
 getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
+const {
+  OTLPTraceExporter,
+} = require('@opentelemetry/exporter-trace-otlp-proto');

const resource = Resource.default().merge(
 new Resource({
   [SemanticResourceAttributes.SERVICE_NAME]: "otel-node-demo",
-    [SemanticResourceAttributes.SERVICE_VERSION]: "0.1.1",
+    [SemanticResourceAttributes.SERVICE_VERSION]: "0.1.2",
 }),
);
const sdk = new NodeSDK({
 resource,
 logRecordProcessor: new SimpleLogRecordProcessor(new OTLPLogsExporter()),
+  traceExporter: new OTLPTraceExporter(),
 instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
</code></code></pre><p>With the changes in place, restart both the collector and the app. Send a request and query for logs in Loki. When the log results show up, you should now be able to jump from log to the corresponding trace.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BWxt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BWxt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!BWxt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!BWxt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!BWxt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BWxt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg" width="1456" height="902" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:902,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Loki logs to traces&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Loki logs to traces" title="Loki logs to traces" srcset="https://substackcdn.com/image/fetch/$s_!BWxt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!BWxt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!BWxt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!BWxt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba78af64-275d-4211-81b6-0e0a8bfa5718_3356x2080.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Note that this also works in reverse.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bc4Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bc4Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bc4Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bc4Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bc4Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bc4Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg" width="1456" height="1033" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1033,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Tempo to Logs&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Tempo to Logs" title="Tempo to Logs" srcset="https://substackcdn.com/image/fetch/$s_!bc4Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 424w, https://substackcdn.com/image/fetch/$s_!bc4Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 848w, https://substackcdn.com/image/fetch/$s_!bc4Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!bc4Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F948f03b9-075f-4274-98ed-7019ac18bf6d_2858x2028.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Final Thoughts</h2><p>It took three years from the inception of OTEL for the logs API to finally hit GA. While SDK maturity currently varies by language, it is now more usable and can bring big benefits.</p><p>In some sense, logging maturity in OTEL has a greater impact on observability than traces. As useful as traces are, most organizations still don't use them. All organizations use logs. By using logs via OTEL, you can even get many of the benefits of tracing (eg. automatic traceid and spanid) without having to store traces.</p><p>And for organizations that make use of all three pillars, OTEL breaks down the barriers between signals and enables autocorrelation between traces, logs, and metrics.</p><p>All this to say - logs are great. OTEL can make them better.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[OpenTelemetry in 2023]]></title><description><![CDATA[4 Years In, OpenTelemetry Delivers on its Promise for Open Observability]]></description><link>https://bit.kevinslin.com/p/opentelemetry-in-2023</link><guid isPermaLink="false">https://bit.kevinslin.com/p/opentelemetry-in-2023</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 28 Aug 2023 14:01:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HYX3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HYX3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HYX3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HYX3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HYX3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HYX3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HYX3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Telescope observing the night sky&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Telescope observing the night sky" title="Telescope observing the night sky" srcset="https://substackcdn.com/image/fetch/$s_!HYX3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HYX3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HYX3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HYX3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f50d889-0872-4180-8581-2dd9bb5e4e2b_1024x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>OpenTelemetry (OTEL) started in 2019 when the two dominant open-source projects around tracing, <a href="https://opentracing.io/">OpenTracing</a> and <a href="https://opencensus.io/">OpenCencus</a>, <a href="https://medium.com/opentracing/a-roadmap-to-convergence-b074e5815289">merged</a> to form <a href="https://opentelemetry.io/">OpenTelemetry</a>. The scope of the combined project expanded beyond tracing to cover all of observability. OTEL's mission is to help organizations deliver <strong>high-quality, ubiquitous, and portable telemetry.</strong></p><p>Four years later, OTEL is making good on its promise. It has to date provided a stable standard for the three pillars of observability (metrics, logs, and traces), a collector that can receive, process, and export telemetry in any environment, and SDKs to instrument code in all major languages. It has also continued to expand its scope and introduced additional standards around semantic conventions and agent management.</p><p>Today, OTEL is the second most active project in the CNCF, behind only Kubernetes in popularity. Its contributors are spread across all major observability vendors and its protocol has near-universal adoption among observability providers.</p><p>In this post, we'll go over where OTEL is today, provide a summary of the various projects that fall under its domain, and talk about where it's going next.</p><div class="poll-embed" data-attrs="{&quot;id&quot;:97446}" data-component-name="PollToDOM"></div><h2>OTEL - What's in a name?</h2><p>OTEL consists mainly of two types of projects: specifications and implementations.</p><p>Specifications are the foundation of OTEL. They describe how telemetry should be captured, collected, processed, and exported. Specifications tend to be vendor-focused as they define the common standard that vendors have to implement to be compatible with OTEL.</p><p>Implementations are the specific client libraries and tooling that are used to handle telemetry data. Implementations tend to be end-user-focused as they consist of the parts that end-users use to instrument their code.</p><p>A project's stability is typically marked on a signal-by-signal basis. Each signal is a specific type of telemetry data - in OTEL, this means metrics, logs, and traces.</p><p>OTEL today consists of the following sub-projects:</p><ul><li><p>OpenTelemetry Specification (specification)</p></li><li><p>OpenTelemetry SDKs (implementation)</p></li><li><p>OpenTelemetry Protocol (specification)</p></li><li><p>OpenTelemetry Collector (implementation)</p></li><li><p>Open Agent Management Protocol (specification)</p></li><li><p>OpenTelemetry Semantic Conventions (specification)</p></li></ul><h2>OpenTelemetry Specification (1.24.0)</h2><p>The <a href="https://opentelemetry.io/docs/specs/otel/overview/">Open Telemetry specification</a> is the foundation of OTEL - it provides the APIs, SDKs, and data models that all other OTEL standards are derived from.</p><p>A brief timeline of its development:</p><ul><li><p>2020-09: Tracing marked <a href="https://medium.com/opentelemetry/opentelemetry-collector-achieves-tracing-stability-milestone-80e34cadbbf5">stable</a></p></li><li><p>2021-11: Metrics marked <a href="https://github.com/open-telemetry/opentelemetry-specification/pull/2104">stable</a></p></li><li><p>2023-04: Logs marked <a href="https://github.com/open-telemetry/opentelemetry-specification/pull/3376">stable</a></p></li></ul><p>In terms of maturity, the OTEL spec is now <strong>stable for all signals</strong>. Because the logging signal was not deemed stable until earlier this year, many OTEL SDKs still do not support logging at this time.</p><h2>OpenTelemetry SDKs (Mixed)</h2><p>The <a href="https://opentelemetry.io/docs/instrumentation/">OTEL SDKs</a> provide client-side instrumentation based on the OTEL spec. For each language-specific SDK, there is a separate maturity level for each signal.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Df4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Df4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 424w, https://substackcdn.com/image/fetch/$s_!_Df4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 848w, https://substackcdn.com/image/fetch/$s_!_Df4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!_Df4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Df4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg" width="1162" height="1008" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1008,&quot;width&quot;:1162,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OTEL SDK Maturity&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OTEL SDK Maturity" title="OTEL SDK Maturity" srcset="https://substackcdn.com/image/fetch/$s_!_Df4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 424w, https://substackcdn.com/image/fetch/$s_!_Df4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 848w, https://substackcdn.com/image/fetch/$s_!_Df4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!_Df4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ae23556-1563-47d0-897f-d8bb29a40250_1162x1008.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p><a href="https://opentelemetry.io/docs/instrumentation/">Current maturity of each SDK</a>.</p></blockquote><h3>Auto Instrumentation</h3><p>Depending on the programming language, some SDKs also support <strong>automatic instrumentation.</strong> This is when the SDK automatically injects signals, mainly traces, into your application without needing to manually instrument code.</p><p>Auto instrumentation doesn't work for compiled languages like <code>go</code> and <code>rust</code>. That said, you can still get automatic trace injection by going outside of the SDK and using <a href="https://logz.io/blog/ebpf-auto-instrumentation-pixie-kubernetes-observability/">eBPF</a> and/or <a href="https://logz.io/blog/what-is-a-service-mesh-kubernetes-istio/">service mesh</a> based tooling.</p><h2>OpenTelemetry Protocol (OTLP - 1.0)</h2><p><a href="https://opentelemetry.io/docs/specs/otlp/">OTLP</a> describes a common wire protocol for delivering observability data. There are two blessed transports in OTLP: http with <a href="https://protobuf.dev/overview/">protocol buffers</a> and <a href="https://grpc.io/">gRPC</a>.</p><p>This spec is considered stable and can be implemented on any service that receives, processes, or exports OTEL data. The spec is implemented by the OpenTelemetry collector and also by agents of observability vendors like Grafana and Datadog.</p><h2>OpenTelemetry Collector (0.83.0)</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pR02!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pR02!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 424w, https://substackcdn.com/image/fetch/$s_!pR02!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 848w, https://substackcdn.com/image/fetch/$s_!pR02!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 1272w, https://substackcdn.com/image/fetch/$s_!pR02!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pR02!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg" width="1456" height="1033" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1033,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OTEL Collector&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OTEL Collector" title="OTEL Collector" srcset="https://substackcdn.com/image/fetch/$s_!pR02!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 424w, https://substackcdn.com/image/fetch/$s_!pR02!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 848w, https://substackcdn.com/image/fetch/$s_!pR02!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 1272w, https://substackcdn.com/image/fetch/$s_!pR02!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e800bba-b95c-4b74-8f37-523eee0ecedc_984x698.svg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <a href="https://opentelemetry.io/docs/collector/">OTEL collector</a> is a vendor-agnostic agent that collects, transforms, and sends observability data.</p><p>The collector consists of the following components:</p><ul><li><p>receivers: push/pull data from different sources</p></li><li><p>processors: transform/filter/enrich/derive data in flight</p></li><li><p>exporters: send data to downstream destinations</p></li><li><p>connectors: connectors are both a receiver and exporter and enable multiple pipelines to be stitched together</p></li><li><p>pipelines: a chain of receiver, zero or more processors, and exporters</p></li><li><p>extensions: components that provide additional capabilities outside of handling telemetry data (eg. basic auth, health checks, etc)</p></li></ul><p>Together, the various components act as an <a href="https://bit.kevinslin.com/p/first-mile-observability-and-the">observability pipeline</a>, allowing you to collect telemetry data from any source, process it in flight, and send it to any destination.</p><p>The OTEL collector consists of two projects, <a href="https://opentelemetry.io/docs/collector/">otel-collector</a> and <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib">otel-collector-contrib</a>. <code>otel-collector</code> contains only the core components of the collector, chiefly logic strictly related to processing OLTP data.</p><p><code>otel-collector-contrib</code> is a kitchen sink of every integration you can imagine, with exporters and receivers for most observability providers present. At the time of this writing, this includes 91 receivers, 48 exporters, and 24 processors.</p><p>End users are advised to use the <a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/cmd/builder">OpenTelemetry Collector Builder</a> to create a custom build of <code>otel-collector-contrib</code> with only components that they need.</p><p>In addition to <code>otel-collector-contrib</code>, vendors like AWS and Splunk provide their own custom <a href="https://opentelemetry.io/docs/collector/distributions/">distributions</a> of OTEL.</p><h2>Open Agent Management Protocol (OpAMP - Beta)</h2><p><a href="https://opentelemetry.io/docs/specs/opamp/">OpAMP</a> is a network protocol for the remote management of agents. This is a recent addition to OTEL, introduced in 2022, and provides a vendor-agnostic standard for controlling fleets of agents. These agents could be instances of the <code>otel-collector</code> or vendor-specific agents that implement OpAMP.</p><p>With OpAMP, you can enable features like dynamic configuration deployments, agent updates, and credential management.</p><p>There is currently a work-in-progress <a href="https://github.com/open-telemetry/opamp-go">go implementation</a> of the OpAMP spec.</p><blockquote><p>NOTE: If you're interested in managing OTEL agents via OpAMP - this is what <a href="https://www.nimbus.dev/">my startup</a> is working on.</p></blockquote><h2>OpenTelemetry Semantic Conventions (1.21.0)</h2><p>The <a href="https://opentelemetry.io/docs/specs/semconv/">OTEL Semantic Conventions</a> define a common set of attributes for observability data. They cover a wide range of areas including cloud resources, databases, exceptions, and systems.</p><p>The semantic conventions are used by the OTEL SDKs and automatically applied for SDKs that support auto instrumentation. Common semantics enables the ability to correlate between different signals and is something I'm especially excited by, the reason for which can (and probably will) be the subject of an entire blog post.</p><h2>Etc</h2><p>OTEL is a massive project. While we covered the flagship projects, there are additional parts of OTEL that deserve a notable mention:</p><ul><li><p><a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/ottl/README.md">OpenTelemetry Transformation Language (OTTL)</a>: a common transformation language for telemetry that is both performant and flexible - currently designed and implemented in <code>otel-collector-contrib</code></p></li><li><p><a href="https://opentelemetry.io/docs/demo/">OTEL Demo</a>: A micro-service-based shopping site that demonstrates a majority of features and language SDKs of OTEL</p></li></ul><h2>Final Thoughts</h2><p>OTEL started as a merger between competing tracing specifications and evolved to become an industry standard for observability. The last four years laid the groundwork for building a common foundation across vendors and tools. The next four will reveal the fruits of that labor.</p><div class="pullquote"><p>Are you having issues adopting OTEL or looking to migrate off your existing observability vendor? Checkout <a href="https://www.nimbus.dev/">Nimbus</a> - we provide a simple enterprise ready onramp into OTEL and vendor neutral observability</p></div><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://bit.kevinslin.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[First Mile Observability and the Rise of Observability Pipelines]]></title><description><![CDATA[When people speak of observability (assuming they talk about it at all), they tend to focus on the "backend" - aka systems responsible for ingesting, storing, and querying observability data (metrics, logs, and traces).]]></description><link>https://bit.kevinslin.com/p/first-mile-observability-and-the</link><guid isPermaLink="false">https://bit.kevinslin.com/p/first-mile-observability-and-the</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 14 Aug 2023 14:00:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2gLM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2gLM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2gLM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2gLM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2gLM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2gLM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2gLM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a ramp to the highway made up of a series of pipes, digital art&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="a ramp to the highway made up of a series of pipes, digital art" title="a ramp to the highway made up of a series of pipes, digital art" srcset="https://substackcdn.com/image/fetch/$s_!2gLM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2gLM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2gLM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2gLM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8404bbdc-639b-43b1-a129-a7b60872b902_1024x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">a ramp to the highway made up of a series of pipes, digital art</figcaption></figure></div><p>When people speak of observability (assuming they talk about it at all), they tend to focus on the "backend" - aka systems responsible for ingesting, storing, and querying observability data (metrics, logs, and traces). But this is only half the story.</p><p>For observability data to make it into these systems, it first needs to be collected, processed, and exported from the infrastructure and services that generate the data to where it will be used. This "front end" of observability is what is known as "first-mile observability". Solutions in this space have grown in scope in recent years due to the increase in volume (and cost) associated with modern observability data.</p><p>In this post, we'll go over first-mile observability, what it is, and the landscape of offerings that exist in this space today.</p><h2>A Series of Pipes</h2><p>At its core, first-mile observability represents systems that facilitate the collection, processing, and exporting of observability data. They're centered around the following components:</p><ul><li><p>receivers: push/pull data from different sources</p></li><li><p>processors: transform/filter/enrich/derive data in flight</p></li><li><p>exporters: send data to downstream destinations</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ojdo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ojdo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ojdo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ojdo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ojdo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ojdo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Observability Pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Observability Pipeline" title="Observability Pipeline" srcset="https://substackcdn.com/image/fetch/$s_!Ojdo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ojdo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ojdo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ojdo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91469f0-d58b-477e-8109-14359878e7bb_2044x940.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>NOTE: Different implementations have different names for these components. I'm using the terminology adopted by the <a href="https://opentelemetry.io/docs/collector/">Open Telemetry (OTEL) collector</a>.</p></blockquote><p>The combination of receivers, zero or more processors, and exporters, come together to create an <strong>observability pipeline</strong>.</p><p>Some examples of common pipelines:</p><ul><li><p>a pipeline that collects all log data and sends 100% of it for archival to S3 but only ERROR logs to datadog</p></li><li><p>a pipeline that adds host and container level information as custom attributes to each data point before sending it upstream</p></li><li><p>a pipeline that removes unused attributes and dimensions from observability data</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4tFq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4tFq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 424w, https://substackcdn.com/image/fetch/$s_!4tFq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 848w, https://substackcdn.com/image/fetch/$s_!4tFq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!4tFq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4tFq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg" width="1456" height="666" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:666,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Example Pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Example Pipeline" title="Example Pipeline" srcset="https://substackcdn.com/image/fetch/$s_!4tFq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 424w, https://substackcdn.com/image/fetch/$s_!4tFq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 848w, https://substackcdn.com/image/fetch/$s_!4tFq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!4tFq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639ce6bc-9edb-427a-8bae-dbbdadb00165_2052x938.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>Example pipeline that filters logs sent to datadog while sending everything to S3</p></blockquote><h2>First Mile Concepts</h2><h3>Agents</h3><p>The most common deployment in first-mile observability is that of an agent. Agents tend to run on the same system that they are collecting data from. They are responsible for pulling in data, doing lightweight aggregations, and then sending the data off to a preset destination.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Tkll!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Tkll!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Tkll!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Tkll!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Tkll!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Tkll!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg" width="1456" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Agent Example&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Agent Example" title="Agent Example" srcset="https://substackcdn.com/image/fetch/$s_!Tkll!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Tkll!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Tkll!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Tkll!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53fde8c9-6ca0-4ec1-8a20-17a584734712_2048x940.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Every observability vendor has a custom agent.</p><h3>Collectors</h3><p>A step up from the agent deployment is the collector deployment. Collectors can be deployed either on the same system that they are monitoring or as a standalone deployment. They aggregate observability data from various sources, can apply basic filters, and route data to one or more destinations.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BcNT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BcNT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 424w, https://substackcdn.com/image/fetch/$s_!BcNT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 848w, https://substackcdn.com/image/fetch/$s_!BcNT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!BcNT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BcNT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg" width="1456" height="664" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:664,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Collector Example&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Collector Example" title="Collector Example" srcset="https://substackcdn.com/image/fetch/$s_!BcNT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 424w, https://substackcdn.com/image/fetch/$s_!BcNT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 848w, https://substackcdn.com/image/fetch/$s_!BcNT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!BcNT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F675c7c8d-cc0e-480a-a40e-fc3d49350957_2048x934.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>FluentD and Fluent Bit are some of the most popular collector options with wide community adoption and extensive integration solutions.</p><h3>Pipelines</h3><p>When a collector grows up, it becomes an <strong>observability pipeline</strong>. Observability pipelines can be deployed on the same system, as a standalone deployment, or as a cloud endpoint. Observability pipelines can receive data from any source, apply near-arbitrary transformations on the data when it is in flight, and export it to any destination.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lvJA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lvJA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 424w, https://substackcdn.com/image/fetch/$s_!lvJA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 848w, https://substackcdn.com/image/fetch/$s_!lvJA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!lvJA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lvJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg" width="1456" height="667" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:667,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Pipeline Example&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Pipeline Example" title="Pipeline Example" srcset="https://substackcdn.com/image/fetch/$s_!lvJA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 424w, https://substackcdn.com/image/fetch/$s_!lvJA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 848w, https://substackcdn.com/image/fetch/$s_!lvJA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!lvJA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fba5b3-3b57-433a-8e83-0b25a41c2066_2048x938.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Vector is a popular open-source pipeline solution written in Rust that has widespread adoption.</p><h2>First Mile Landscape</h2><p>There are many solutions for observability pipelines - we'll go over some of the most popular solutions in both the open source and commercial sides of this space.</p><h2>Open Source Solutions</h2><p>In terms of open source, popular solutions include <a href="https://fluentbit.io/">Fluent Bit</a>, <a href="https://vector.dev/">Vector</a>, and the Otel Collector</p><h3>Fluent Bit</h3><ul><li><p>released: 2015</p></li><li><p>language: C</p></li><li><p>pros: performance, Lua based transformations</p></li><li><p>cons: smaller ecosystem (compared to FluentD), harder to extend</p></li></ul><p>Fluent Bit was released in 2015 as a lightweight version of FluentD. It has zero dependencies, is highly performant, and supports arbitrary transformations that can be authored with the Lua programming language. It also comes <a href="https://aws.amazon.com/about-aws/whats-new/2019/11/aws-launches-firelens-log-router-for-amazon-ecs-and-aws-fargate/">built-in</a> for certain cloud vendors like AWS.</p><h3>Vector</h3><ul><li><p>released: 2020</p></li><li><p>language: Rust</p></li><li><p>pros: performance, custom transformation language (VRL), acknowledgment and delivery <a href="https://vector.dev/docs/about/under-the-hood/guarantees/">guarantees</a></p></li><li><p>cons: smaller ecosystem, hard to write plugins</p></li></ul><p>Vector was released in 2020 as a modern rust-based observability pipeline. It achieves even higher throughput than Fluent Bit (albeit with <a href="https://medium.com/ibm-cloud/log-collectors-performance-benchmarking-8c5218a08fea">higher CPU consumption</a>) and is easy to install and integrate. Users can transform data using the <a href="https://vector.dev/docs/reference/vrl/">vector remap language</a> - a custom DSL that is designed for transforming data while maximizing flexibility, runtime safety, and performance.</p><h3>OTEL Collector</h3><ul><li><p>released: 2020</p></li><li><p>language: Go</p></li><li><p>pros: performance, large ecosystem, industry standard, easy to extend</p></li><li><p>cons: mixed stability</p></li></ul><p>The OTEL collector was released in 2020 as part of the Open Telemetry (OTEL) project. OTEL is a CNCF initiative to provide a vendor-neutral standard for cloud-native observability. The OTEL collector can be deployed and used as either an agent, collector, or pipeline depending on how it's configured.</p><p>The OTEL collector is available in many different flavors. The <a href="https://github.com/open-telemetry/opentelemetry-collector">core collector</a> provides a stable core of built-in receivers, processors, and exporters to facilitate OTEL formatted data. The <a href="https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib">OTEL Contrib Distro</a> contains the kitchen sink of integrations into every vendor and observability service under the sun. Vendors like <a href="https://docs.splunk.com/Observability/gdi/opentelemetry/resources.html">Splunk</a> and <a href="https://aws.amazon.com/otel/">AWS</a> also offer their own curated OTEL Distros that are specifically curated for their particular platform.</p><p>Users can transform data using built-in processors or use the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/ottl/README.md">OpenTelemetry Transformation Language</a> (OTTL). This is a SQL-like declarative DSL optimized for transforming observability data. Note that OTTL is under active development.</p><h2>Commercial Solutions</h2><p>There are also multiple commercial offerings available in the observability pipeline space. Popular vendors include <a href="https://cribl.io/">Cribl</a>, <a href="https://calyptia.com/">Calyptia</a>, <a href="https://www.datadoghq.com/">Datadog</a>, and <a href="https://www.mezmo.com/">Mezmo</a>.</p><h3>Cribl</h3><ul><li><p>founded: 2017</p></li></ul><p>Cribl was started by a group of ex-splunk folks. Their original claim to fame was saving money on your Splunk bill. Cribl is known as the first major commercial offering in the observability pipeline space. They are an easy choice for the enterprise and companies that are heavily reliant on Splunk.</p><h3>Calyptia</h3><ul><li><p>founded: 2020</p></li></ul><p>Calyptia was founded by the authors of FluentD and FluentBit. They provide an observability pipeline solution built on top of said products. They are a good choice if you already use these technologies or operate on platforms that integrate natively with them.</p><h3>Datadog</h3><ul><li><p>founded: 2010, acquired Vector in 2021</p></li></ul><p>Datadog has dominated the modern observability landscape by positioning itself as the single pane of (expensive) glass for all observability needs. They acquired the company behind Vector in 2021 and have built a managed observability pipelines solution around it. They are a good choice if you're already using Vector and want native support for datadog.</p><h3>Mezmo (formerly LogDNA)</h3><ul><li><p>founded: 2015, rebranded to Mezmo in 2022</p></li></ul><p>Mezmo started as LogDNA with an exclusive focus on providing a logging pipeline. It has since expanded its scope and also rebranded itself to be a general observability pipeline solution. Mezmo is easy to get started and has a slick UI for adding filters and transformation. They have a proprietary agent for their logging pipeline but their observability pipeline can ingest data from a variety of different sources, including fluentd, fluent bit, and OTEL.</p><h3>Nimbus</h3><ul><li><p>founded: 2023</p></li></ul><p>Nimbus was started in 2023 by the author. It is a fully managed observability pipeline that helps organizations reduce data volume by 60% without losing signal. Nimbus analyzes your traffic and automatically recommends optimizations that reduce and aggregate data. </p><h2>Choosing the Right Solution</h2><p>There are many options for first-mile observability. The easiest and most common choice is to do nothing and use whatever agent(s) comes bundled with your vendor/solution of choice.</p><p>If that no longer suffices, some considerations for choosing an observability pipeline below:</p><ul><li><p>For something performant that just works, consider Vector. Use DataDog for a managed version.</p></li><li><p>For integrating with the widest set of existing tools, consider FluentD and Fluent Bit. Use Calyptia for a managed version.</p></li><li><p>For enterprise and Splunk buildouts, consider Cribl. There is no open-source equivalent though Cribl does offer a free tier.</p></li><li><p>For a good mix of everything as well as future-proofing with Open Telemetry, use the OTEL collector.</p></li></ul><h2>Final Thoughts</h2><p>In 2023, not only is observability data growing at a dizzying rate, but so are vendors that help you manage it. Whereas the "backend" space has a vast selection of options for both incumbents and newcomers, the "frontend" space did not develop commercially until 2017 with the launch of Cribl. Now six years later, observability pipelines are only now starting to gain traction. I'm personally excited by the potential of OTEL - its CNCF status, standards-based implementation, and transformation language position it to be a dominant leader in this space.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The Architecture of Modern Observability Platforms]]></title><description><![CDATA[An observability platform is an end-to-end system that helps organizations understand the health of their applications and services.]]></description><link>https://bit.kevinslin.com/p/the-architecture-of-modern-observability</link><guid isPermaLink="false">https://bit.kevinslin.com/p/the-architecture-of-modern-observability</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Wed, 26 Jul 2023 14:01:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mHZA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mHZA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mHZA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!mHZA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!mHZA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!mHZA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mHZA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1499650,&quot;alt&quot;:&quot;modern observability platform, digital art&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="modern observability platform, digital art" title="modern observability platform, digital art" srcset="https://substackcdn.com/image/fetch/$s_!mHZA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!mHZA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!mHZA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!mHZA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F179c40f2-6453-43e2-b612-358e961a6c5c_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">modern observability platform, digital art</figcaption></figure></div><p>An observability platform is an end-to-end system that helps organizations understand the health of their applications and services. This understanding comes from the three pillars of modern observability: metrics (time series data), logs (text-based data), and traces (request data with associated baggage/metadata).</p><p>The challenge of modern-day observability is scale - instead of a single host running a LAMP stack generating a few megabytes of observability data per day, we now have Kubernetes clusters with thousands of services generating gigabytes of observability data every hour.</p><p>The challenge of collecting, ingesting, storing, and querying observability data at scale is what modern observability platforms are designed to tackle. Depending on the underlying architecture, the cost of running these platforms can vary by a <strong>factor of over 100x</strong>. This post covers different types of architectures and solutions that implement them.</p><h2>The Observability Pipeline</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KRoG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KRoG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 424w, https://substackcdn.com/image/fetch/$s_!KRoG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 848w, https://substackcdn.com/image/fetch/$s_!KRoG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!KRoG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KRoG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg" width="1286" height="583" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:583,&quot;width&quot;:1286,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Observability Pipeline Stagse&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Observability Pipeline Stagse" title="Observability Pipeline Stagse" srcset="https://substackcdn.com/image/fetch/$s_!KRoG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 424w, https://substackcdn.com/image/fetch/$s_!KRoG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 848w, https://substackcdn.com/image/fetch/$s_!KRoG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!KRoG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F841a3aa4-3206-4d61-ac44-345eb9d48158_1286x583.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Before diving into architecture, an overview of concepts. There are four distinct phases when it comes to making use of observability data:</p><ul><li><p>collection: observability data is received at the edge (usually in the form of an agent running on a host)</p></li><li><p>ingestion: observability data is processed at the destination (usually involves batching, compression, and additional transformations to get the data in the optimal shape for storage)</p></li><li><p>storage: observability data is retained (usually involves indexing)</p></li><li><p>query: observability data is looked up (usually involves translating a query to a GET/LIST request to the underlying storage system)</p></li></ul><h2>Observability Architectures</h2><h3>Distinct and Separate Components</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JSLa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JSLa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 424w, https://substackcdn.com/image/fetch/$s_!JSLa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 848w, https://substackcdn.com/image/fetch/$s_!JSLa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 1272w, https://substackcdn.com/image/fetch/$s_!JSLa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JSLa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png" width="1456" height="669" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:669,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87579,&quot;alt&quot;:&quot;distinct and separate&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="distinct and separate" title="distinct and separate" srcset="https://substackcdn.com/image/fetch/$s_!JSLa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 424w, https://substackcdn.com/image/fetch/$s_!JSLa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 848w, https://substackcdn.com/image/fetch/$s_!JSLa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 1272w, https://substackcdn.com/image/fetch/$s_!JSLa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F821e0fed-fc15-4a1c-a608-bd0a082aa0ba_1554x714.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As observability evolved from checking <code>syslog</code> to dedicated systems devoted to monitoring, early platforms build highly specialized services for each of the observability pillars. This meant that metrics, logs, and traces were processed by completely independent systems with distinct collection, storage, and query layers.</p><p>In the open-source world, this might take the form of running <a href="https://en.wikipedia.org/wiki/Prometheus_(software)">Prometheus</a>, <a href="https://en.wikipedia.org/wiki/Elasticsearch">Elasticsearch</a>, and <a href="https://www.jaegertracing.io/">Jaeger</a>. The pipeline for each service is listed below.</p><p>Prometheus for metrics</p><pre><code><code>collect (prometheus scraper) -&gt; ingest(prometheus) -&gt; store (prometheus) -&gt; query (prometheus)
</code></code></pre><p>Elasticsearch for logs</p><pre><code><code>collect (logstash) -&gt; ingest (elasticsearch) -&gt; store (elasticsearch) -&gt; query (elasticsearch)
</code></code></pre><p>Jaeger for traces</p><pre><code><code>collect (jaeger collector) -&gt; ingest (jaeger) -&gt; store (cassandra) -&gt; query (jaeger)
</code></code></pre><p>Observability vendors that were founded during this time (eg. splunk, new relic, datadog) were likely built on a similar foundation (of distinct and separate services for each pillar).</p><blockquote><p>NOTE: While the specifics of the implementation that vendors use are not public knowledge, their pricing model, both the dollar value as well as the dimensions that they charge for (eg. the amount of data indexed) suggest they use this architecture</p></blockquote><h3>Unified Collection</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j5Yy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j5Yy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 424w, https://substackcdn.com/image/fetch/$s_!j5Yy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 848w, https://substackcdn.com/image/fetch/$s_!j5Yy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 1272w, https://substackcdn.com/image/fetch/$s_!j5Yy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!j5Yy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png" width="1456" height="664" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:664,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138602,&quot;alt&quot;:&quot;unified collection&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="unified collection" title="unified collection" srcset="https://substackcdn.com/image/fetch/$s_!j5Yy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 424w, https://substackcdn.com/image/fetch/$s_!j5Yy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 848w, https://substackcdn.com/image/fetch/$s_!j5Yy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 1272w, https://substackcdn.com/image/fetch/$s_!j5Yy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc5dd307-e19a-4ffb-8cd4-8bc0e1d73058_1558x710.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As observability grew in scope (and vendors in abundance), standards started emerging. This culminated with Open Telemetry (OTEL) being released in 2019 from the merger of the two big projects in this space: <a href="https://opentracing.io/">OpenTracing</a> and <a href="https://opencensus.io/">OpenCensus</a>.</p><p>In 2023, this standard has (mostly) come into maturity - they provide a vendor-neutral spec and implementation to collect metrics, logs, and traces from any source and send them to any destination.</p><p>All observability vendors today have support for OTEL.</p><h3>Unified Storage</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dGHd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dGHd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dGHd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dGHd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dGHd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dGHd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg" width="1276" height="588" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:588,&quot;width&quot;:1276,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Unified Storage&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Unified Storage" title="Unified Storage" srcset="https://substackcdn.com/image/fetch/$s_!dGHd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dGHd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dGHd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dGHd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc4bf451-0713-4fa6-b479-748872b69bdc_1276x588.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As observability data grew more voluminous, folks started to realize that the approach of using high-performant databases to store and index all this data did not scale.</p><p>Instead of indexing everything upfront in an expensive database, you can get order-of-magnitude improvements in cost per byte by only doing a partial index and storing the data in cloud object storage solutions like S3 (usually with gzip + parquet).</p><p>In the open-source world, you can find this architecture in the following solutions:</p><ul><li><p>Prometheus with <a href="https://cortexmetrics.io/">Cortex</a>/<a href="https://github.com/grafana/mimir">Mimir</a> backend for metrics</p></li><li><p><a href="https://grafana.com/oss/loki/">Loki</a> for logs</p></li><li><p><a href="https://grafana.com/oss/tempo/">Tempo</a> for traces</p></li></ul><p>Observability vendors that use an object storage backend: <a href="https://logz.io/">logz</a>, <a href="https://grafana.com/products/cloud/">grafana cloud</a>, and <a href="https://axiom.co/">axiom</a>.</p><blockquote><p>NOTE: <a href="https://clickhouse.com/">Clickhouse</a> is a high-performance open-source columnar database that is also a popular solution for storing metrics, logs, and traces. One downside with clickhouse is that it couples the storage layer with the query layer which means that fundamental decoupling of the two (which we'll cover later in this article) is infeasible in clickhouse-based architectures.</p></blockquote><h3>Unified Ingestion</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dPq4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dPq4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dPq4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dPq4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dPq4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dPq4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg" width="1280" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Unified Ingestion&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Unified Ingestion" title="Unified Ingestion" srcset="https://substackcdn.com/image/fetch/$s_!dPq4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 424w, https://substackcdn.com/image/fetch/$s_!dPq4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 848w, https://substackcdn.com/image/fetch/$s_!dPq4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!dPq4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde0cfc46-fed7-4bda-8fe7-33198a9e3468_1280x582.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When all observability data is stored with unified storage, it becomes possible to also radically simplify its ingestion.</p><p>Ingestion is difficult at scale because of data volumes, throughput, and sudden changes in traffic patterns. Instead of standing up separate services to ingest data coming in for each pillar, you can consolidate everything using a streaming platform like Kafka. These platforms are designed to ingest data in real-time at scale and also allow real time enrichment and transformation of the data as it comes in.</p><p>I don't know of any open-source observability projects that use this architecture. Presumably, this is because you only start accruing benefits once you unify all three pillars with a common storage backend - otherwise, it's more efficient to build out a dedicated ingestion mechanism than taking the overhead of standing up something like Kafka</p><p>The observability vendor <a href="https://coralogix.com/">Coralogix</a> employs a <a href="https://coralogix.com/how-it-works/">unified streaming-based architecture</a> that does something similar.</p><h3>Unified Querying</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9Ot-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9Ot-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9Ot-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9Ot-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9Ot-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9Ot-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg" width="1280" height="588" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:588,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Unified Querying&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Unified Querying" title="Unified Querying" srcset="https://substackcdn.com/image/fetch/$s_!9Ot-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 424w, https://substackcdn.com/image/fetch/$s_!9Ot-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 848w, https://substackcdn.com/image/fetch/$s_!9Ot-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!9Ot-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16c89ec6-afb3-45d2-8ed0-b109dd33bd4c_1280x588.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When all observability data is stored with unified storage, it becomes possible (and often necessary) to simplify the querying.</p><p>Querying terabytes of data is expensive when the data is indexed and hard to do at an acceptable latency when it is not. This is where massively parallel processing (MPP) techniques come into play - by spinning up many processors to independently process data (using tools like spark and Trino) and coupled with a metadata store like a hive or Apache Iceberg, it becomes possible to process terabytes of "un-indexed" data in seconds.</p><p>Like with ingestion, I don't know of any open-source observability platforms that use this architecture. The reason for this is similar to that of ingestion - unless you build out an observability platform that is made from day one to support all the pillars with unified storage, it's not worth the overhead of standing up something like Spark or Trino. </p><p>The observability vendor <a href="https://www.observeinc.com/">Observe</a> employs a <a href="https://www.observeinc.com/blog/how-observe-uses-snowflake-to-deliver-the-observability-cloud-part-1/">unified query architecture</a> using Snowflake as its MPP engine.</p><h2>Final Thoughts</h2><p>There's a lot of observability data in the world and many platforms that enable you to make use of it. This is all at a price - the major driver of that price is the underlying architecture of the observability platform (and the margins that vendors want to make).</p><p>The unification of the storage layer enabled the ability to scale observability platforms while reducing costs by an order of magnitude. The unification of the ingestion layer and the querying layer has the potential to bring about similar benefits.</p><p>There's an opportunity today to create a massively more scalable and affordable observability platform by unifying every layer of the observability pipeline. In a <a href="https://bit.kevinslin.com/p/youre-paying-too-much-for-cloudwatch">prior post</a>, we show how doing this for logs can bring down cost by over <strong>95%</strong> when compared to datadog. </p><p>This sort of unified architecture effectively commodifies observability at scale. This is a good thing as the amount of data that is observed continues to grow - at the rate we're going, even <a href="https://blog.pragmaticengineer.com/datadog-65m-year-customer-mystery/">enterprise</a> companies are starting to look for alternative solutions due to costs.</p><p>All this said, being able to have the data on hand is just table stakes - the real value is being able to translate that information into insights and business outcomes. The sooner we can stop worrying about cost, the sooner we can get back to doing just that.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Athena Provisioned Capacity Review]]></title><description><![CDATA[Was it worth the wait?]]></description><link>https://bit.kevinslin.com/p/athena-provisioned-capacity</link><guid isPermaLink="false">https://bit.kevinslin.com/p/athena-provisioned-capacity</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Thu, 20 Jul 2023 14:01:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jIUn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jIUn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jIUn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!jIUn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!jIUn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!jIUn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jIUn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1592291,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jIUn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!jIUn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!jIUn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!jIUn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83ced88f-d937-41dc-b63a-725936bd6dbd_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">bits of data waiting in a queue in a factory, digital art </figcaption></figure></div><p>Athena released <a href="https://aws.amazon.com/blogs/aws/introducing-athena-provisioned-capacity/">provisioned capacity</a> in April 2023. This has been one of the most asked-for features ever since Athena launched in 2016. In this post, we'll go over what provisioned capacity is, why it matters, and how it holds up in practice.</p><p>As a refresher, <a href="https://aws.amazon.com/athena/">Athena</a> is a serverless analytics offering by AWS that "provides a simplified, flexible way to analyze petabytes of data where it lives". Underneath the hood, Athena runs on <a href="https://trino.io/">presto/trino</a>.</p><p>Athena is pure serverless - it has zero setup and you only get billed when you query. Billing for Athena is based on the data scanned and is charged at $5/TB or $0.005/GB (for the keen-eyed, this is the same price as <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html">cloudwatch log insights</a>).</p><p>Note that there are additional fees due to GET/PUT calls to S3 and <a href="https://aws.amazon.com/glue/">AWS Glue</a> - these costs are generally negligible and don't meaningfully impact the query cost (though they'll frustrate your accountant).</p><p>Athena runs on shared infrastructure and has the concept of a <strong>queue time</strong> - depending on the load on the Athena fleet in a particular region, you might have to wait before your query gets to run. Most of the time, queue time is under 200ms but for the p99 case, this can go past a minute.</p><p>Query execution itself is quite fast, especially on the latest <a href="https://docs.aws.amazon.com/athena/latest/ug/engine-versions-reference-0003.html">Athena engine </a>. On a well-tuned dataset, Athena can scan +7GB/s and finish in single-digit seconds for simple queries, making it suitable for real-time analysis.</p><p>While there is both extensive documentation and controls for <a href="https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/">tuning</a> both your dataset and your queries for optimal performance, there has not been, until this year, a way of directly controlling your time in the queue.</p><h2>Athena Provisioned Capacity</h2><p>Athena provisioned capacity gives customers a way of reserving capacity on the Athena fleet for exclusive use. By reserving sufficient capacity, you can <strong>eliminate queuing and also have predictable billing</strong>.</p><p>When you provision capacity, you request capacity in <strong>Data Processing Units (DPUs)</strong>. A single DPU is equivalent to 4 vCPUs and 16GB of memory. It is charged at $0.30/DPU.</p><p>You have to specify at least 8 DPUs when making a reservation and you need to book it for at least 8h. This comes out to $7.20/hour or $57.60 minimum per use. Once you make a reservation, <strong>you are no longer charged for the amount of data scanned but purely for the DPUs that you've reserved</strong>.</p><p>After 8h of use, DPUs are charged on a per-minute basis. You can cancel a reservation at any time but if you cancel early, you will still be charged for a minimum of 8h of use.</p><h2>Making a Reservation</h2><p>Making a reservation is simple and can be done either via the console or the API. Note that reservations can not go through depending on capacity as well as take up to 30 minutes to be ready. That said, in testing, reservations were successful in under a minute.</p><p>Once a reservation is made, you need to associate it with one or more workgroups. A workgroup is an Athena concept that encapsulates the Athena engine version, capabilities, and limits on querying for users of the given workgroup.</p><p>After completing the association, any queries you make via a workgroup that has a capacity reservation will use the dedicated capacity instead of the on-demand capacity of the Athena fleet.</p><h2>Using Provisioned Capacity</h2><p>The good news is that provisioned capacity does eliminate unexpected queue spikes - because of dedicated capacity, you are guaranteed execution (as long as there is capacity).</p><p>This comes however with the caveat that <strong>average queue time gets worse</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CoPW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CoPW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 424w, https://substackcdn.com/image/fetch/$s_!CoPW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 848w, https://substackcdn.com/image/fetch/$s_!CoPW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!CoPW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CoPW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg" width="924" height="988" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:988,&quot;width&quot;:924,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;On Demand vs Reserved&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="On Demand vs Reserved" title="On Demand vs Reserved" srcset="https://substackcdn.com/image/fetch/$s_!CoPW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 424w, https://substackcdn.com/image/fetch/$s_!CoPW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 848w, https://substackcdn.com/image/fetch/$s_!CoPW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!CoPW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2eca7234-6f11-4459-9931-fc70001771bd_924x988.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The above image shows observed queue times when running in an on-demand workgroup vs a provisioned workgroup. The average time spent in the queue is <code>~158ms</code> for on-demand and <code>~331ms</code> for provisioned (more than 2x). The difference of an extra ~150ms per query vs the variance introduced by an additional 6000ms during a queue spike is generally worth the tradeoff - nevertheless, if you're running lots of short queries, this is something to be aware of.</p><p>What is not great about provisioned capacity is that your <strong>time in the queue can grow unbounded</strong> (for as long as you have an active reservation). This happens if you do not have enough DPUs provisioned. Any workgroup that uses provisioned capacity is <strong>limited to only using provisioned capacity</strong> as long as the reservation is in place. This means if your in-flight queries are consuming all the DPUs, any additional queries will be queued until capacity becomes available.</p><p>AWS Lambda is another serverless offering with a provisioned capacity model. Unlike the Athena model, Lambda's <a href="https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html">provisioned concurrency</a> allocates pre-initialized environments based on the reservation and falls back to on-demand when it is exhausted. It's an approach that I wish Athena would have followed - falling back to on-demand is a much better user experience than putting a hard stop on queries.</p><p>Effectively, provisioned capacity turns Athena from a serverless analytics engine to managed infrastructure that you have to plan capacity around. What makes this worse is the lack of visibility when it comes to mapping DPUs to workloads.</p><p>While AWS has a table that provides guidance on how many DPUs to reserve based on the expected number of concurrent queries, this is not enough information to do capacity planning.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JRyv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JRyv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 424w, https://substackcdn.com/image/fetch/$s_!JRyv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 848w, https://substackcdn.com/image/fetch/$s_!JRyv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!JRyv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JRyv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg" width="1456" height="490" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:490,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Athena Provisioned Capacity Recommendations&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Athena Provisioned Capacity Recommendations" title="Athena Provisioned Capacity Recommendations" srcset="https://substackcdn.com/image/fetch/$s_!JRyv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 424w, https://substackcdn.com/image/fetch/$s_!JRyv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 848w, https://substackcdn.com/image/fetch/$s_!JRyv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!JRyv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88d3a0a3-aa6f-4720-a812-6e511c23cc2a_1462x492.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is because DPU requirement varies based on <strong>data size, storage format, query construction, and other factors</strong>, not just concurrency. As an example, the <a href="https://registry.opendata.aws/commoncrawl/">common-crawl dataset</a> has partitions that exceed 200GB. Running the following select statement requires a minimum of 124 DPUs (this translates to 1984GB of RAM - why is this much RAM needed to scan 200GB of data? I can't tell you why and neither can Athena &#129335;&#8205;&#9794;&#65039;).</p><pre><code><code>select url from ccindex
WHERE crawl = 'CC-MAIN-2023-06'
AND subset = 'warc' AND content_digest = '3WCXFWELSS4SO3MLCODIDHS2KLT4SHPZ'
</code></code></pre><p>Right-sizing how many DPUs you need is a manual guess and check - the <a href="https://docs.aws.amazon.com/athena/latest/ug/capacity-management-requirements.html">current AWS recommendation</a> is to reduce DPUs and see if that causes query failures or increases queue times.</p><h2>Final Thoughts</h2><p>I like Athena. It is one of my favorite AWS services and the first thing I reach for when needing to analyze data in S3. The team has done a tremendous job in boosting engine performance and building out a wide feature set. It even works across regions, accounts, and <a href="https://docs.aws.amazon.com/athena/latest/ug/encryption.html">KMS-encrypted objects</a>. The issue of unpredictable queue spikes is one of the few remaining reservations I have about the service.</p><p>Provisioned capacity is the first direct solution to this problem. Unfortunately, by using it, you also give up most of the benefits of Athena being serverless. With provisioned capacity, you now have to plan for capacity upfront, you pay even when you're not using the service, and you hit a hard stop if you exceed the set capacity.</p><p>I do not doubt that there exists a set of customers that will be well served with this feature - mainly those with consistent workloads that take place over eight hours and place a premium on predictable pricing. For everyone else, you're probably better served continuing with on-demand.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by Bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[You're Paying too much for (Cloudwatch) Logs]]></title><description><![CDATA[Reducing Cloudwatch Log Costs by 80% with Firehose, S3 and Athena]]></description><link>https://bit.kevinslin.com/p/youre-paying-too-much-for-cloudwatch</link><guid isPermaLink="false">https://bit.kevinslin.com/p/youre-paying-too-much-for-cloudwatch</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 10 Jul 2023 14:00:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LthL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LthL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LthL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!LthL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!LthL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!LthL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LthL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1579151,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LthL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!LthL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!LthL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!LthL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddda488e-e1ff-4fed-b6d2-d8f101016246_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">a pile of money being burned on top of a stack of logs, digital art</figcaption></figure></div><p>If you're using AWS, there's a good chance (&gt;95%) you're using <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html">Cloudwatch Logs (CL)</a>. It is the defacto logging solution from AWS - CL lets you collect, store, and query log files from anywhere with little to no upfront work.</p><p>If you're using CL, there's also a good chance you're paying for more than you need.</p><h2>Why Cloudwatch Logs</h2><p>First, lets talk about why you might be using CL.</p><p>CL comes ready out of the box. You might not even be aware that you're using it as it integrates directly with 30+ AWS services (API Gateway, Lambda, ECS, etc).</p><p>CL can also be extended into on-prem environments using the <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html">unified cloudwatch agent</a> - you can use the agent to collect both metrics and logs from the host machine.</p><p>CL covers the basic use cases for logging - querying (via <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html">Cloudwatch Log Insight</a>) and tailing (via <a href="https://bit.kevinslin.com/p/how-real-time-is-amazon-cloudwatch">live tail</a>). It also lets you use filters to create metrics from logs and add subscribers that can forward your logs to other destinations.</p><p>CL's most compelling (and expensive) feature is that it can do all of this in "near real time" - logs usually show up ~5s-20s after publication (CL publishes times for when logs are emitted vs ingested so you can actually do the calculation yourself).</p><h2>Why not Cloudwatch Logs</h2><p>CL might not be for you if you don't need "near real time" logs and the cost of operation is too high.</p><p>This is because the real time nature of CL comes at a premium - the ingestion fee per gigabyte ($0.50/GB) is one of the highest in the industry (for comparison, new relic is $0.30/GB, datadog is $0.10/GB, and S3 is free).</p><p>CL charges you based on ingestion and retention. Ingestion is $0.50/GB. Retention is $0.03/GB. Ideally, you'd want the costs to be flipped. When retention is expensive, you can reduce costs by shortening you retention policy and archiving older logs to S3. But when ingestion is expensive, your options are limited and generally involve not sending logs at all.</p><h2>Comparing Cloudwatch Logs with Other Solutions</h2><p>Lets compare CL to some other logging solutions in terms of cost. Specifically, we'll compare:</p><ul><li><p>cost of using CL</p></li><li><p>cost of using Datadog (industry standard)</p></li><li><p>cost of a custom solution using other AWS services</p></li></ul><p>We'll make the following assumptions to baseline our comparison:</p><ul><li><p>Net new logs ingested per day: 500GB/day</p></li><li><p>Average size of a single log event: 256 Bytes</p></li><li><p>Retention period for logs: 90 days</p></li><li><p>Amount of data scanned per day: 3,500GB (7 days worth of logs)</p></li></ul><p>When calculating log costs, we'll consider the following factors:</p><ul><li><p>ingestion fee: how much does it cost to import the logs</p></li><li><p>retention fee: how much does it cost to store the logs</p></li><li><p>querying fee: how much does it cost to search the logs</p></li></ul><h3>1. Cloudwatch Logs</h3><ul><li><p>Ingestion: $0.50/gb -&gt; for 500GB/day, this is $250/day.</p></li><li><p>Retention: $0.03/gb -&gt; for 500GB/day, this is $15/day</p></li><li><p>Querying (via Cloudwatch Log Insight): $0.005/GB scanned -&gt; for 3500GB, this is $17.50/day</p></li><li><p>Total: $282.50/day</p></li></ul><p>Note that the retention cost is less than 10% of ingestion cost. CL is expensive primarily because of the high ingest cost.</p><h3>2. Datadog</h3><ul><li><p>Ingestion: $0.10/GB -&gt; $50/day</p></li><li><p>Retention: $1.70/1 million events -&gt; assuming 256B per event, 500GB translates to 1,953,125,000 events or $3,320.31day</p></li><li><p>Querying: $0</p></li><li><p>Total: $3,370.313/day</p></li></ul><p>Note that Datadog's pricing is flipped when compared to CL - ingestion is cheap but retention is very expensive (~<strong>2200%</strong> more in this case). Your exact costs will vary depending on the size of your event but in most cases, you'll pay more for retention with Datadog than with any other vendor.</p><h3>3. Custom Solution using other AWS Services</h3><p>There's many ways to build a logging platform on top of AWS. For today's comparison, we will use <a href="https://aws.amazon.com/kinesis/data-firehose/">Firehose</a> (managed streaming service), <a href="https://aws.amazon.com/s3/">S3</a> (managed object storage), and <a href="https://aws.amazon.com/athena/">Athena</a> (managed query/analytics engine on top of S3). These are all mature serverless offerings provided by AWS that require no ongoing operational effort to run and are also cost effective at scale.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rJat!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rJat!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rJat!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rJat!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rJat!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rJat!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg" width="1082" height="356" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:356,&quot;width&quot;:1082,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Firehose to S3 to Athena&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Firehose to S3 to Athena" title="Firehose to S3 to Athena" srcset="https://substackcdn.com/image/fetch/$s_!rJat!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rJat!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rJat!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rJat!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6531bc8-e0e7-48c4-b6ea-58a3f09db261_1082x356.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Calculating the cost is a bit more involved and differs per service.</p><ul><li><p>Ingestion:</p><ul><li><p>Firehose: $0.029/GB -&gt; $14.50/day</p></li><li><p>S3: Ingest is free but you do pay $0.005 per 1k PUT request. Assuming you set the buffer at 1MB for Firehose, this works out to 500GB/0.001GB * $0.005/1000 or $2.50/day</p></li><li><p>Total: $14.50/day</p></li></ul></li><li><p>Retention: $0.023/GB for S3 standard storage or $11.50/day</p></li><li><p>Querying:</p><ul><li><p>Athena: $0.005/GB scanned or $17.50/day</p></li><li><p>S3: Athena issues GET requests to S3 which costs $0.0004 per 1k GET request. Assuming average object size of 1MB, this works out to 3500GB/0.001GB * $0.0004/1000 or $1.40/day</p></li></ul></li><li><p>Total: $47.40/day</p></li></ul><p>Our home grown solution made from combining different AWS services stands out in a few respects compared to the other solutions. Most notably, it is <strong>5x cheaper than CL</strong> and <strong>98% cheaper than Datadog</strong> for the same amount of logs. More importantly, there is no premium on either ingestion or retention which means that costs stay manageable even as you grow.</p><p>One caveat is that our home grown solution has a variable cost when it comes to querying data. How much this varies depends on how your data is stored as Athena charges per GB scanned. This is impacted by your file format (eg. Parquet, Iceberg, etc), indexing, and partition strategies. It is also depends on what kind of queries you run and how often you run them.</p><p>The other caveat is that the latency of streaming logs from Firehose to S3 can be on the order of minutes instead of seconds so it might not work as well if you need near real time logs.</p><p>Finally, this doesn't have the other nice features that CL has - mainly, metric filters and subscriptions.</p><p>Note that depending on your requirements, any and all of these caveats can be addressed. You can eliminate the query cost by using <a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ultrawarm.html">UltraWarm</a> storage with Opensearch. You can get near real time logs, metric filters, and subscriptions by using Kinesis Data Streams instead of Firehose. Each of these solutions have different cost/complexity tradeoffs but will, at scale, still be cheaper than running CL or Datadog.</p><h3>Costs of Different Logging Solutions</h3><p>Here's a table that shows the various cost calculations in one place.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FFwi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FFwi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 424w, https://substackcdn.com/image/fetch/$s_!FFwi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 848w, https://substackcdn.com/image/fetch/$s_!FFwi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 1272w, https://substackcdn.com/image/fetch/$s_!FFwi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FFwi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png" width="1456" height="172" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:172,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68816,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FFwi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 424w, https://substackcdn.com/image/fetch/$s_!FFwi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 848w, https://substackcdn.com/image/fetch/$s_!FFwi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 1272w, https://substackcdn.com/image/fetch/$s_!FFwi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde05d5a5-3727-4da6-b16e-ccb68dacba85_2178x258.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>Final Thoughts</h2><p>When you're starting out, everything is cheap (even datadog). You can and should use whatever works best for your team and business.</p><p>This article is not advocating that you not use CL - it's a fantastic service (with questionable UX) that takes a lot of toil out of logging. But if you find that log costs are getting you down and especially if you're not taking advantage of the near real time nature of CL, then know that there are better options.</p><p>Everything in AWS is about tradeoffs - if you take time to optimize your usage, you will not find a most cost competitive solution at scale (without running your own data centers). The challenge is that it takes a long time to even know what all the options are, never mind actually put them into practice (it doesn't help that AWS releases changes on a <strong>daily basis</strong>).</p><p>In the case of Cloudwatch Logs, understand the tradeoff you're implicitly making and if cost is a concern, make different tradeoffs.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How Real-Time is Amazon Cloudwatch Logs Live Tail?]]></title><description><![CDATA[AWS released Amazon CloudWatch Logs Live Tail on June 6th, 2023.]]></description><link>https://bit.kevinslin.com/p/how-real-time-is-amazon-cloudwatch</link><guid isPermaLink="false">https://bit.kevinslin.com/p/how-real-time-is-amazon-cloudwatch</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Thu, 29 Jun 2023 14:02:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G8V-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!G8V-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!G8V-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!G8V-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!G8V-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!G8V-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!G8V-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1587721,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!G8V-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!G8V-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!G8V-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!G8V-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F250fa011-3668-4c1b-bfa3-9037ddb7ce58_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">bullet time with light speed travel background , digital art</figcaption></figure></div><p>AWS released <a href="https://aws.amazon.com/about-aws/whats-new/2023/06/live-tail-amazon-cloudwatch-logs">Amazon CloudWatch Logs Live Tail</a> on June 6th, 2023. The name is on point even if it is a mouthful (though terse by <a href="https://aws.amazon.com/documentdb/">AWS standards</a>) - Live Tail is a service that helps developers follow (tail) <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html">cloudwatch logs</a> in <strong>real-time</strong>. It also layers on additional goodies like dynamic filters and highlights.</p><p>The <strong>real-time</strong> part of the service intrigued me - cloudwatch logs, anecdotally, tend to have a latency between 5s to 20s for log ingestion. Could Live Tail do better? And how does it compare to the existing CLI workflow?</p><pre><code><code>aws logs tail {LogGroupName} --follow --format json</code></code></pre><blockquote><p>NOTE: <code>aws logs tail</code> is <strong>not</strong> equivalent to <strong>Live Tail</strong> and has been available long before</p></blockquote><p>Experiment time!</p><h2>Setup</h2><p>To run the test, I set up a serverless stack consisting of API Gateway + Lambda.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nWhU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nWhU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nWhU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nWhU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nWhU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nWhU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg" width="1176" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:1176,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;API Gateway to Lambda&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="API Gateway to Lambda" title="API Gateway to Lambda" srcset="https://substackcdn.com/image/fetch/$s_!nWhU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nWhU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nWhU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nWhU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb381e879-fa8b-4892-952b-5283f5933908_1176x616.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The experiment:</p><ol><li><p>Use both <strong>Live Tail</strong> and the <strong>AWS CLI</strong> to tail the created cloudwatch logs</p></li><li><p>Send an HTTP request to API Gateway</p></li><li><p>Observe when logs showed up in live tail vs the CLI</p></li></ol><p>To measure when logs showed up, I split my window into three panes.</p><ul><li><p>Pane 1 represented the Live Tail console</p></li><li><p>Pane 2 represented the results of running the CLI</p></li><li><p>Pane 3 was opened to https://currentmillis.com which showed the current time in milliseconds</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y_6M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y_6M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 424w, https://substackcdn.com/image/fetch/$s_!y_6M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 848w, https://substackcdn.com/image/fetch/$s_!y_6M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!y_6M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y_6M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg" width="1456" height="844" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:844,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Live Tail and CLI Setup&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Live Tail and CLI Setup" title="Live Tail and CLI Setup" srcset="https://substackcdn.com/image/fetch/$s_!y_6M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 424w, https://substackcdn.com/image/fetch/$s_!y_6M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 848w, https://substackcdn.com/image/fetch/$s_!y_6M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!y_6M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75e96f85-cac3-4b5c-833e-276ca6376869_3002x1740.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I recorded my screen to track when logs showed up in panes 1 and 2. I did this testing across two scenarios - the first from a <a href="https://lumigo.io/blog/this-is-all-you-need-to-know-about-lambda-cold-starts/">lambda cold start</a> (this first created a new cloudwatch <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html">log stream</a>), the second from a warm start (this appended logs to the existing stream)</p><p>You can see a sample run below</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;588287cf-59f4-4442-ba04-b9b912697391&quot;,&quot;duration&quot;:null}"></div><p>I then compared this data to the output of the <a href="https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_GetLogEvents.html">GetLogEvents</a> API call. This returns events from a particular log group in the following format:</p><pre><code><code>{
  "events": [
    {
      "timestamp": 1687921318305,
      "message": "INIT_START Runtime Version: nodejs:16.v14\tRuntime Version ARN: arn:aws:lambda:us-east-1::runtime:***\n",
      "ingestionTime": 1687921325842,
    },
    ...
}
</code></code></pre><ul><li><p><code>timestamp</code>: the time when the event was emitted (from lambda)</p></li><li><p><code>ingestionTime</code>: the time when the event was processed (in cloudwatch logs)</p></li></ul><blockquote><p>TIP: By subtracting the <code>ingestionTime</code> from the <code>timestamp</code>, you get the cloudwatch log delivery latency.</p></blockquote><h2>Observations</h2><p>The initial call from cold start yielded the following timestamps (in milliseconds since epoch). Timestamps are space-delimited for readability.</p><pre><code><code>call: 16879213 18 305,
inge: 16879213 25 842
tail: 16879213 27 193
foll: 16879213 27 193</code></code></pre><p>The way to read the timestamps:</p><ul><li><p>call: when API call was made (<code>timestamp</code> field)</p></li><li><p>inge: when log was ingested by cloudwatch (<code>ingestionTime</code>)</p></li><li><p>tail: when Live Tail started displaying the logs</p></li><li><p>foll: when the AWS CLI first started displaying the logs</p></li></ul><p>Based on the initial data, you can see that Live Tail is downstream of cloudwatch logs. Ingestion of logs has a 7s latency. Tailing of the logs has a 2s additional latency. In this case, both tailing and following logs occurred within the same second.</p><p>Here is the result of another run, this time from a warm start.</p><pre><code><code>call2: 16879213 38 725 
inge2: 16879213 47 744 
tail2: 16879213 50 096
foll2: 16879213 55 079</code></code></pre><p>This time, there's a 9s ingestion latency followed by a 3s latency for Live Tail. The CLI has another additional 5s latency behind tailing.</p><p>I re-ran this experiment a few times and got similar results. Live Tail will stream logs ~2s from when it is first ingested in cloudwatch. Using the CLI, this can vary between 2s to 9s. Note that this is on top of the latency to ingest metrics into cloudwatch logs (usually between 5s-20s).</p><h2>Implementation</h2><p>Behind the scenes, I assumed that Live Tail was using <a href="https://en.wikipedia.org/wiki/WebSocket">websockets</a>. This is hinted at by the pricing model (seconds of active usage) which charged for the number of active connections.</p><p>This can be verified by digging into the Chrome network inspector tab. At the start of a Live Tail session, the console makes the following request.</p><pre><code><code># NOTE: extraneous fields stripped for clarity
curl 'https://us-east-1.console.aws.amazon.com/cloudwatch/CloudWatch/data/auth.SdkSign/20230628033119884-***' \
  -H 'origin: https://us-east-1.console.aws.amazon.com' \
  ...
  --data-raw $'{
    "Service":"logs","Endpoint":"logs.us-east-1.amazonaws.com","HttpMethod":"POST","Region":"us-east-1","Path":"/",
    "Headers":{"X-Amz-User-Agent":"aws-sdk-js/2.1265.0 promise","X-Amz-Requested-Operation":"startLiveTail","Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"Logs_20140328.StartLiveTail",...}'
</code></code></pre><p>Note the <code>X-Amz-Target</code> set to <strong>Logs_20140328.StartLiveTail</strong>. This looked very much like an API call to start a WebSocket connection.</p><p>When inspective for websocket connections, you can see said connection</p><pre><code><code>curl 'wss://us-east-1.prod.livetail.cloudwatchlogs.aws.dev/' \
  -H 'Origin: https://us-east-1.console.aws.amazon.com' \
  -H 'Sec-WebSocket-Key: **' \
  -H 'Upgrade: websocket' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: Upgrade' \
  -H 'Sec-WebSocket-Version: 13' \
  -H 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits' \
</code></code></pre><p>Presumably, there is a real-time service in cloudwatch logs that will push new events through the Live Tail websocket as soon as it is ingested.</p><p>In this regard, Live Tail is unlike most AWS services. AWS has a reputation for being an API-first platform (Route53 launched with only API support back in 2010). It feels strange to see a new feature be launched that is <em>console first</em> - at the time of this writing, there is no way (that I know of) to integrate Live Tail via API.</p><h2>Parting Thoughts</h2><p>Live Tail is a service that works as advertised. It tails your cloudwatch logs in <em>real-time</em> with the caveat that it is subject to the same ingestion latency that is inherent in being downstream of cloudwatch logs.</p><p>While the CLI-based solution has existed for ages, Live Tail brings this to the AWS console with additional features (eg. the ability to tail multiple log groups, filtering, and syntax highlighting). The difference between Live Tail and the <code>tail</code> cli command is that one is push-based and the other is pull-based. Live Tail can deliver logs from cloudwatch with consistent single-second latencies whereas, via the CLI, it is variable.</p><p>If you're already using the CLI, there's not much incentive to switch (unless you need the ability to tail multiple log groups at once). If you rely on the console for log diving, then Live Tail will be a welcome addition to your tool set.</p><p>Do note that Live Tail is not free. It has a heart palpitation-inducing per-second pricing model ($0.01 per minute) that is offset, in part, by the 1,800 free minutes per month free tier (that's 1h per day).</p><p>All in all, the addition of Live Tail fills the last gap in the cloudwatch suite of observability tooling. Just remember to not leave it running for too long!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The S3 Balancing Act: Security vs Simplicity]]></title><description><![CDATA[On April 27, 2023, AWS rolled out a long-anticipated change to Amazon S3.]]></description><link>https://bit.kevinslin.com/p/the-s3-balancing-act-security-vs</link><guid isPermaLink="false">https://bit.kevinslin.com/p/the-s3-balancing-act-security-vs</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Thu, 08 Jun 2023 13:01:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!GCiT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GCiT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GCiT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!GCiT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!GCiT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!GCiT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GCiT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two buckets being balanced&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two buckets being balanced" title="Two buckets being balanced" srcset="https://substackcdn.com/image/fetch/$s_!GCiT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!GCiT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!GCiT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!GCiT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0faf7eea-964f-4ee6-9344-6b450002b0d1_1024x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>On April 27, 2023, AWS <a href="https://aws.amazon.com/blogs/aws/heads-up-amazon-s3-security-changes-are-coming-in-april-of-2023/">rolled out a long-anticipated change</a> to Amazon S3. This change blocked public access and custom ACL rules, by default, for all newly created buckets on S3.</p><p>Bad S3 bucket permissions are among the most widely exploited "<a href="https://www.bitdefender.com/blog/businessinsights/worst-amazon-breaches/">security incidents</a>" in the cloud. This occurs when someone creates a bucket for private data (eg. personal customer information) but accidentally makes that bucket publicly available. By blocking public access, AWS makes this sort of misconfiguration impossible (without explicitly disabling the block policy).</p><p>But no good deed goes unpunished - for every step forward AWS makes security-wise, it feels that they go two steps back on the developer experience (DX). While public buckets can no longer be created <strong>by accident</strong>, they are also now much more difficult to create <strong>on purpose</strong>.</p><p>This is a problem because creating public buckets is a common use case for hosting static sites and assets - this workflow is so common that it is one of the <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html">featured workflows</a> for S3.</p><p>Let's illustrate what the change looks like by trying to create a public bucket. If you're using the AWS console, you now must explicitly disable the "Block all public access" toggle as well as check a consent dialogue to create a bucket. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RZBB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RZBB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RZBB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RZBB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RZBB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RZBB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg" width="1456" height="1234" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1234,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:408501,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RZBB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RZBB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RZBB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RZBB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff93264d0-2398-4e3a-b325-ad995c4c864e_2048x1736.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Note that this is for ensuring that a bucket <strong>can be</strong> public - you still need to apply the proper object or bucket-level policies to make objects public.</p><p>If you're a DevOps wizard that never touches the console, you might create a new bucket using Terraform or the <a href="https://aws.amazon.com/cdk/">CDK</a>.</p><p>In this case, you might write the following code:</p><pre><code><code>const publicBucket = new s3.Bucket(this, 'MyPublicBucket', {
  publicReadAccess: true,
});
</code></code></pre><p>When you run a diff, things look normal</p><pre><code><code>$ cdk diff
Stack AutoCdkStack
IAM Statement Changes
&#9484;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;   &#9474; Resource                &#9474; Effect &#9474; Action       &#9474; Principal &#9474; Condition &#9474;
&#9500;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
&#9474; + &#9474; ${MyPublicBucket.Arn}/* &#9474; Allow  &#9474; s3:GetObject &#9474; AWS:*     &#9474;           &#9474;
&#9492;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[+] AWS::S3::Bucket MyPublicBucket MyPublicBucketBCB7A648 
[+] AWS::S3::BucketPolicy MyPublicBucket/Policy MyPublicBucketPolicyDE00CFED 


&#10024;  Done in 3.94s.
</code></code></pre><p>Try deploying it, however, and you run into an unhelpful "Access Denied" error with no further context</p><pre><code><code>$ cdk deploy

&#10024;  Synthesis time: 2.88s

AutoCdkStack:  start: Building 46900dc420169a4d5dc632927ff6ca3ba8c1ca93cdd60512a3c0950e2d75
...
AutoCdkStack: deploying... [1/1]
AutoCdkStack: creating CloudFormation changeset...
7:37:38 AM | CREATE_FAILED        | AWS::S3::BucketPolicy | MyPublicBucketPolicyDE00CFED
API: s3:PutBucketPolicy Access Denied

 &#10060;  AutoCdkStack failed: Error: The stack named AutoCdkStack failed to deploy: UPDATE_ROLLBACK_COMPLETE: API: s3:PutBucketPolicy Access Denied
...
The stack named AutoCdkStack failed to deploy: UPDATE_ROLLBACK_COMPLETE: API: s3:PutBucketPolicy Access Denied
</code></code></pre><p>The error comes from the change in default bucket policy - when you create a new bucket and want to make it public, you also must disable the "Block Public Access" settings on the bucket.</p><p>In our cdk example, this would look like the following</p><pre><code><code>    new s3.Bucket(this, 'MyPublicBucket', {
      publicReadAccess: true,
      blockPublicAccess: { 
&#9;      blockPublicAcls: true, blockPublicPolicy: false, 
&#9;      ignorePublicAcls: true, restrictPublicBuckets: false},
    });
</code></code></pre><p>Unfortunately, this isn't well documented in the CDK docs. You will also run into similar problems when trying to provision a public bucket using Terraform. As of 2023-06-10, Both Terraform and CDK have S3 bucket creation tickets as the number one pinned issues in their respective repositories, <a href="https://github.com/aws/aws-cdk/issues/25358">here</a> and <a href="https://github.com/hashicorp/terraform-provider-aws/issues/28353">here</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mCVC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mCVC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mCVC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mCVC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mCVC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mCVC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg" width="1456" height="360" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:360,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AWS CDK Repo Pinned Issue&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AWS CDK Repo Pinned Issue" title="AWS CDK Repo Pinned Issue" srcset="https://substackcdn.com/image/fetch/$s_!mCVC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mCVC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mCVC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mCVC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d7cad90-eaae-42e0-a00c-b553f83261b2_2048x506.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><blockquote><p>AWS CDK</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u_ed!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u_ed!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 424w, https://substackcdn.com/image/fetch/$s_!u_ed!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 848w, https://substackcdn.com/image/fetch/$s_!u_ed!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!u_ed!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u_ed!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg" width="1456" height="552" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:552,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Terraform pinned issue&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Terraform pinned issue" title="Terraform pinned issue" srcset="https://substackcdn.com/image/fetch/$s_!u_ed!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 424w, https://substackcdn.com/image/fetch/$s_!u_ed!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 848w, https://substackcdn.com/image/fetch/$s_!u_ed!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!u_ed!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5a64423-d90a-4f5d-ac2c-c7add09a802b_2048x777.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>Terraform</p></blockquote><p>While the issue with public S3 Buckets seems minor, it is just the tip of the iceberg in terms of complexity. Creating a public/private bucket is a trivial example. Things become more complicated once you involve encrypted objects, other AWS services, and cross-account access. I wrote an article <a href="https://www.kevinslin.com/notes/f2542b0c-5cbd-49b9-84d8-151ccab99dea/">many moons ago</a> about debugging eight different permission layers to diagnose an S3 bucket permission issue. Since that article was written, S3 has added even more <s>complexity</s> features like <a href="https://aws.amazon.com/s3/features/object-lambda/">object lambdas</a> and <a href="https://aws.amazon.com/s3/features/access-points/">access points</a> that add more layers to an already complicated access control model.</p><p>Zooming out - S3 is a single service. AWS has over 200 services. Each service is complicated by itself but that complexity compounds when they need to talk to one another. It then further compounds once you involve multiple accounts.</p><p>So what do we do about this?</p><p>Some solutions address the complexity by building platforms that abstract away the cloud provider. Services like Heroku and Vercel do a great job to improve DX <strong>outside of the cloud</strong>. They provide this DX at the cost of control and extensibility (plus significant markups in pricing).</p><p>Other solutions address the complexity by building better tooling <strong>inside the cloud</strong>. Infrastructure-as-Code (IaC) primitives like Terraform, CDK, and Pulumi make it easier to programmatically create infrastructure and replicable environments. Unfortunately, they still require users to deal with all the messy implementation details of each service. The latest addition in the IaC space is <a href="https://winglang.io/">Winglang</a>, a new project made by the <a href="https://twitter.com/emeshbi?lang=en">original creator</a> of the AWS CDK. It aims to be a higher-level IaC primitive that further abstracts many of the messy details of cloud services and is to terraform what Python is to C.</p><p>The change in S3 permission defaults highlights the challenges of building a cloud that is simple, secure, and extensible. This can feel like a CAP theorem-like problem but there are no laws of physics that prevent us from having all three. I think there's a lot more that can be done in this space, particularly the approach of improving the DX inside the cloud vs offering a limited experience outside of it. This is a space I have a few ideas on which I aim to share in the coming weeks. Meanwhile, may your clouds be unfettered and your buckets secure (even if they are not simple).</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Lessons from Creating a VSCode Extension with GPT-4 (Part II)]]></title><description><![CDATA[The more things change, the more they stay the same]]></description><link>https://bit.kevinslin.com/p/lessons-from-creating-a-vscode-extension</link><guid isPermaLink="false">https://bit.kevinslin.com/p/lessons-from-creating-a-vscode-extension</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Thu, 01 Jun 2023 14:01:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HQji!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HQji!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HQji!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HQji!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HQji!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HQji!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HQji!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a cyberpunk illustration of a robot getting a memory augmentation, digital art&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="a cyberpunk illustration of a robot getting a memory augmentation, digital art" title="a cyberpunk illustration of a robot getting a memory augmentation, digital art" srcset="https://substackcdn.com/image/fetch/$s_!HQji!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HQji!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HQji!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HQji!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd78af739-a1ff-40c7-ac14-00014cc860fa_1024x1024.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>NOTE: This is Part II of a blog series that explores writing code with LLMs. See <a href="https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the">here</a> for Part I of the series.</p></blockquote><p>This post continues where we left off in <a href="https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the">Part I</a>. It builds on the previous work to generate a fully functional vscode extension without human intervention.</p><p>All the code used in this experiment is open source and published on GitHub. Specifically:</p><ul><li><p>A fork of <a href="https://github.com/smol-ai/developer/">smol-ai</a> customized for creating VS Code extensions: <a href="https://github.com/kevinslin/smol-vscode-developer/">smol-vscode-developer</a></p></li><li><p>VS Code extension: <a href="https://github.com/kevinslin/adjust-heading-level.git">repository</a>, <a href="https://marketplace.visualstudio.com/items?itemName=dendron.adjust-heading-level">published extension</a></p></li></ul><h2>Recap</h2><p>Previously, we used GPT4 to generate code to create a vscode extension from scratch. This involved lots of iteration and manual intervention. Most of the issues came from a lack of coherence (variables generated in one file were named something different in another file) and GPT's knowledge cutoff date (it's only been trained on data that was available in September 2021).</p><p>To make up for these deficiencies, we start with an updated prompt that accounts for all the issues we encountered in part I.</p><p>Prompt from part I:</p><pre><code><code>the app is: a vscode extension that lets the user adjust the heading level of the selected text. it should have three commands: increase heading level, decrease heading level, and set heading level

Important details:

- make sure there is a tsconfig.json file present
- created a shared `utils.ts` file that exports the `adjustHeadingLevel` method to adjust headings
- in `package.json`, use the `1.6.1` dependency for `vscode-test`
- make sure to include `.vscode/launch.json` as one of the mandatory files
    - it should have a "extensionHost" launch task
    - it should have "--disable-extensions" as one of the "args"
</code></code></pre><p>Updated Prompt:</p><pre><code><code>a vscode extension that lets the user adjust the heading level of the selected text. it should have three commands: increase heading level, decrease heading level, and set heading level
created a shared `utils.ts` file that exports the `adjustHeadingLevel` method to adjust headings
when registering commands, use an identifier that would match the following regex: "extension.(increaseHeading|decreaseHeading|setHeading)"


Important details:
- make sure the following files are present:
    - tsconfig.json
    - .gitignore
    - README.md
    - src/test/runTest.ts
- make sure the following constraints are observed in ".gitignore"
    - ignore javascript files
- make sure the following constraints are observed in "runTest.ts"
    - import "mocha" directly (eg. `import Mocha from 'mocha'`)
    - import glob as named export (eg. `import {glob} from 'glob'`)
    - do not import modules that are not needed
    - use node builtin "assert" library for assertions
    - DO NOT use parameters that have implicit 'any' type - add proper types or add an explicit 'any' type
- make sure the following shared dependencies are present
    - mocha for testing
- make sure that the README has the following sections:
    - Overview: what this project is about
    - Quickstart: how to use this extension
    - Development Guide: how to develop this extension
- make sure the following dependencies are present in `package.json`
    - @vscode/test-electron: ^2.3.2
    - glob: ^7.1.4
    - dependencies required for testing (eg. mocha)
    - dependencies required for types (eg. @types/mocha, @types/glob)
- make sure the following dependencies are not present in `package.json`
    - "vscode-test"
- make sure the following constraints are observed in `package.json`
    - when referring to an extension command, use the format "extension.{command-name}". DO NOT use the extension name as the command prefix
    - when adding "activationEvents", do not include "onCommand" directives
- make sure to include `.vscode/launch.json` as one of the mandatory files
    - it should have a "extensionHost" launch task
    - it should have "--disable-extensions" as one of the "args"
    - the "preLaunchTask" task should be "npm: compile"
- make sure that you have tests for the user-specified functionality
- for all typescript code, generate correct imports according to `"esModuleInterop": true` is set in `tsconfig.json`
    - when exporting modules, ALWAYS use a named export, NEVER a default export. if the module is the only export in the file, the module name MUST BE IDENTICAL to the filename 
    - when importing modules that we have written, ALWAYS import the names of modules we are using
    - when importing modules, ONLY import modules that will be used 
    - when calling a function from 'shared_dependencies', ALWAYS use the given type signature
</code></code></pre><p>There are a lot of changes - let's go over each section and explain what's happening.</p><h2>Specifying Files</h2><p>We start by specifying all the files that GPT might miss. Files like '.gitignore' are not core to creating a vscode extension but necessary for developers to sanely manage a git repo. We include them directly in the prompt since we don't want developers to have to add these "boilerplate" details every time.</p><pre><code><code>- make sure the following files are present:
    - tsconfig.json
    - .gitignore
    - README.md
    - test/runTest.ts
</code></code></pre><h2>Specifying File Contents</h2><p>We also need to add additional constraints depending on the file.</p><p>For example, the <code>.gitignore</code> won't always ignore javascript unless explicitly specified in the prompt.</p><pre><code><code>- make sure the following constraints are observed in ".gitignore"
    - ignore javascript files
</code></code></pre><p>We also encounter issues when generating a README - whether or not it covers setup instructions is not deterministic. We can make it so by being prescriptive over what sections we expect.</p><pre><code><code>- make sure that the README has the following sections:
    - Overview: what this project is about
    - Quickstart: how to use this extension
    - Development Guide: how to develop this extension
</code></code></pre><h2>Specifying Dependencies</h2><p>We explicitly list out dependencies that GPT might miss. These involve dependencies introduced after GPT's cutoff date (eg. <code>@vscode/test-electron</code>) and development dependencies</p><pre><code><code>- make sure the following shared dependencies are present
    - mocha for testing
- make sure the following dependencies are present in `package.json`
    - @vscode/test-electron: ^2.3.2
    - glob: ^7.1.4
    - dependencies required for testing (eg. mocha)
    - dependencies required for types (eg. @types/mocha, @types/glob)
</code></code></pre><blockquote><p>NOTE: Having examples matters. Without the <code>(eg. ...)</code> in parenthesis, GPT generates NO dev dependencies</p><pre><code><code>    - @vscode/test-electron: ^2.3.2
-   - dependencies required for testing 
+   - dependencies required for testing (eg. mocha)
-   - dependencies required for types 
+   - dependencies required for types (eg. @types/mocha)
</code></code></pre></blockquote><p>We also list dependencies that GPT should not include. These are dependencies that have been deprecated since GPT's cutoff date.</p><pre><code><code>- make sure the following dependencies are not present in `package.json`
    - "vscode-test"
</code></code></pre><h2>Specifying Custom Constraints</h2><p>We explicitly tell GPT about changes that are not in its index. In this case, the fact that VSCode no longer requires <code>onCommand</code> to be part of the extension manifest. We also prescribe how extensions are identified in the app.</p><pre><code><code>- make sure the following constraints are observed in `package.json`
    - when referring to an extension command, use the format "extension.{command-name}". DO NOT use the extension name as the command prefix
    - when adding "activationEvents", do not include "onCommand" directives
</code></code></pre><blockquote><p>NOTE: Order matters. Using the mental model of "if this then that" helps with specifying constraints. For the diff below, the first directive did not work but the second does.</p><pre><code><code>-   - do not include "onCommand" directives when adding "activationEvents"
+   - when adding "activationEvents", do not include "onCommand" directives
</code></code></pre></blockquote><p>We also need to explicitly tell GPT about the <code>preLaunchTask</code> as it has the tendency to generate <code>npm: watch</code> when left to its own devices. Because the watch command never exits, launching the app in development mode would hang otherwise</p><pre><code><code>- make sure to include `.vscode/launch.json` as one of the mandatory files
    - it should have a "extensionHost" launch task
    - it should have "--disable-extensions" as one of the "args"
    - the "preLaunchTask" task should be "npm: compile"
</code></code></pre><h2>Specifying Code</h2><p>We explicitly tell GPT about what coding style to use. This is because GPT has no context for the contents of the files that it generates. Javascript has multiple ways of exporting a module (eg. named export vs default export). GPT will more likely than not use one style for exporting and a different one for importing.</p><pre><code><code>- for all typescript code, generate correct imports according to `"esModuleInterop": true` is set in `tsconfig.json`
    - when exporting modules, ALWAYS use a named export, NEVER a default export. if the module is the only export in the file, the module name should be identical to the filename
    - when importing modules that we have written, ALWAYS import the names of modules we are using
    - when importing modules, ONLY import modules that will be used 
</code></code></pre><h2>Specifying Type Signatures</h2><p>We provide additional details when specifying shared dependencies. Just generating the function names is not enough as GPT will hallucinate different ways of calling the shared function.</p><p>The following is a modification of the original prompt to generate explicit type annotations for any shared functions.</p><pre><code><code>...
 Now that we have a list of files, we need to understand what dependencies they share.
 Name and briefly describe what is shared between the files we are generating, including exported variables, data schemas, and function signatures.
 Exclusively focus on the names of the shared dependencies, and do not add any other explanation. 
+ For function signatures, include the function name and the input and output parameters. Add type annotations to the function signatures
</code></code></pre><p>This adds the following annotation to shared functions.</p><pre><code><code>Shared dependencies:
- - adjustHeadingLevel (function name in utils.ts)
+ - adjustHeadingLevel function signature: adjustHeadingLevel(text: string, adjustment: number): string
</code></code></pre><blockquote><p>NOTE: If you are generating code that does not have type signatures, you can still tweak the prompt to generate "typescript types for shared dependencies"</p></blockquote><h2>Specifying Test Details</h2><p>We need to specify many more implementation details for testing. There are more changes here than in other sections for the following reasons:</p><ul><li><p>VSCode made a <a href="https://code.visualstudio.com/api/working-with-extensions/testing-extension#migrating-from-vscode">major change</a> in how tests should be written (after GPT's knowledge cutoff)</p></li><li><p>the libraries used in testing (eg. <code>glob</code> and <code>mocha</code>) have changed major versions and function signatures since GPT knowledge cut off</p></li><li><p>GPT does not generate dependencies for tests unless explicitly prompted</p></li></ul><p>The following prompt tries to address most of these issues. We hardcode example values and get very prescriptive about what to do and not do</p><pre><code><code>- make sure the following constraints are observed in "runTest.ts"
    - import "mocha" directly (eg. `import Mocha from 'mocha'`)
    - import glob as named export (eg. `import {glob} from 'glob'`)
    - do not import modules that are not needed
    - use node builtin "assert" library for assertions
    - DO NOT use parameters that have implicit 'any' type - add proper types or add an explicit 'any' type
- make sure that you have tests for the user-specified functionality
</code></code></pre><blockquote><p>NOTE: Despite the hints, the tests unfortunately still do not compile most of the time. I timeboxed how long I could spend getting this to work which means getting GPT to correctly generate tests will be an exercise left to the reader</p></blockquote><h2>Results</h2><p>Running the prompt at the top of this writeup <strong>should</strong> result in a working vscode extension (most of the time). You can pull down the code from <a href="https://github.com/kevinslin/smol-vscode-developer/">here</a> and follow the <code>README</code> to try it out yourself.</p><blockquote><p>NOTE: The generated tests might need manual tweaking - the extension itself should function.</p></blockquote><p>You can find the output of our generation <a href="https://github.com/kevinslin/adjust-heading-level">here</a>. This is also published in the vscode marketplace and you can download the extension <a href="https://marketplace.visualstudio.com/items?itemName=dendron.adjust-heading-level">here</a> to try it out yourself.</p><p>To check if this would work for a different task, I also generated an extension to format markdown. The prompt is as follows:</p><pre><code><code>a vscode extension that lets the user bold, italicise and strikethrough the selected markdown text. it should have three commands: "bold selection", "italicise selection", and "strikethrough selection"
when registering commands, use an identifier that would match the following regex: "extension.(boldSelection|italiciseSelection|strikethroughSelection)"

...
</code></code></pre><p>The <code>...</code> contains the same prompt details that we covered in this write-up. This worked on the first try (with the caveat that the tests do not and require tweaking)</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M60L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M60L!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!M60L!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!M60L!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!M60L!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M60L!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif" width="1456" height="940" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:940,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Markdown Formatting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Markdown Formatting" title="Markdown Formatting" srcset="https://substackcdn.com/image/fetch/$s_!M60L!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!M60L!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!M60L!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!M60L!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F338cf23a-89c9-4b6e-a8c2-3966b109b7ed_1672x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fBu6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fBu6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!fBu6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!fBu6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!fBu6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fBu6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif" width="1456" height="940" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:940,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:667520,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fBu6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!fBu6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!fBu6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!fBu6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda4c70ca-496d-45a1-be52-d0abe2e2340f_1672x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Code for this extension <a href="https://github.com/kevinslin/markdown-text-styling-extension">here</a></p><h2>Lessons Learned</h2><ul><li><p>little changes in the prompt have big changes in the outcome</p><ul><li><p>ORDER Matters. GPT is a next-word predictor. It reads context from left to right. When you want GPT to do something, tell it when it should act before what it should do (eg. "when X happens, do Y" works much better than "do Y when X happens")</p></li><li><p>Examples help. Instead of saying <code>include test dependencies</code>, bias with an example <code>include test dependencies (eg. mocha)</code></p></li></ul></li><li><p>watch out for non-deterministic outputs</p><ul><li><p>Be prescriptive when there is ambiguity. Treat GPT as a smart intern. Tell it exactly what to do, especially when there are multiple ways of doing something (eg. how to import/export modules in javascript)</p></li><li><p>Use types. This builds on being prescriptive. When you want GPT to reuse a function, make sure the context includes the function signature</p></li></ul></li><li><p>do not trust and always verify</p><ul><li><p>Verify the output. Ideally using automated tests. Even when you're explicit about everything, there is still a chance that GPT makes things up. In our case, even when the type signature of <code>adjustHeadingLevel</code> was set, GPT still generated an invalid usage of the function when creating a unit test</p></li></ul></li></ul><h2>Parting Thoughts</h2><p>The approach we took to generate code is what I call the "do-then-check" strategy. We tell GPT to do a thing, GPT does the thing, we check if the thing is right, and then we add anything that wasn't right as additional instructions to the prompt so that GPT does the thing right the next time.</p><p>Getting a functional solution for even a basic app using this strategy requires a huge amount of context. This is to overcome the following problems:</p><ul><li><p>dealing with information that is not in the LLM index (past its knowledge cutoff date)</p></li><li><p>dealing with "coherence" - making sure that code generated across different runs did not conflict</p></li></ul><p>Both issues reduce to the same issue - how do you find the right information to add to a limited context window?</p><p>Coincidentally, this is a problem space that I've been working in for the past decade. My <a href="https://www.dendron.so/">initial startup</a> was built around the mission of helping humans organize and make sense of information. Given that the information we have available far exceeds our own context window, how do we find the right information when needed?</p><p>I strongly believe that these problems are related. It is likely that the same means by which humans can deal with <a href="https://www.kevinslin.com/notes/e1455752-b052-4212-ac6e-cc054659f2bb/">information overload</a> also apply to LLMs.</p><p>Much more on this topic in the near future &#128519;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Lessons from Creating a VSCode Extension with GPT-4]]></title><description><![CDATA[Lately, I've been playing around with LLMs to write code.]]></description><link>https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the</link><guid isPermaLink="false">https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Thu, 25 May 2023 14:00:10 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!0vzt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0vzt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0vzt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!0vzt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!0vzt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!0vzt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0vzt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1845583,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0vzt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!0vzt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!0vzt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!0vzt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c91a709-7a96-47b7-9b72-7ab6bd65ee70_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Lately, I've been playing around with LLMs to write code. I find that they're great at generating small self-contained snippets. Unfortunately, anything more than that requires a human to evaluate LLM output and come up with suitable follow-up prompts. Most examples of "GPT wrote X" are this - a human serves as a REPL for the LLM, carefully coaxing it to a functional result. This is not to undersell this process - it's remarkable that it works. But can we go further? Can we use an LLM to generate ALL the code for a complex program ALL at once without any human intervention?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Writing a VSCode Extension</h2><p>To test GPT-4's ability to generate a complex program, I prompted it to create a VSCode extension that lets the user adjust the heading level of selected Markdown text. This task requires:</p><p>- Domain-specific knowledge about how to scaffold and expose a program to VSCode</p><p>- Mixing multiple languages and platforms: VS Code extensions are written in TypeScript, which requires writing configuration for Typescript, Node.js, and VSCode</p><p>- Generating multiple files </p><p>- Generating scaffolding to debug, build, and run code </p><h2>Setup</h2><p>For this experiment, I used GPT-4 for all generation purposes. I find it to be the most effective among current-day models.</p><p>In addition, I make use of the <a href="https://github.com/smol-ai/developer">smol-ai</a> framework to generate code.</p><p><code>smol-ai</code> description from the README:</p><blockquote><p>This is a prototype of a "junior developer" agent (aka smol dev) that scaffolds an entire codebase out for you once you give it a product spec, but does not end the world or overpromise AGI. instead of making and maintaining specific, rigid, one-shot starters, like create-react-app, or create-nextjs-app, this is basically create-anything-app where you develop your scaffolding prompt in a tight loop with your smol dev.</p></blockquote><p>I like <code>smol-ai</code> because of its simplicity. The entire code generation logic is in a single Python file consisting of three primary functions:</p><ol><li><p>generate a list of files that are needed to carry out the prompt (eg. package.json, index.js, ...)</p></li><li><p>generate a list of shared dependencies that are needed to carry out the prompt (eg. axios, react, ...)</p></li><li><p>for each file in the generated file list, generate code that would go into the file, making use of shared dependencies if applicable</p></li></ol><p>Note that <code>smol-ai</code> also provides other capabilities like the ability to debug errors and generate a prompt based on the current code base. These are not explored in this post.</p><h2>Prompt</h2><p>I started with a basic prompt describing my problem statement</p><pre><code><code>python main_no_modal.py "a vscode extension that lets the user adjust the heading level of the selected text. it should have three commands: increase heading level, decrease heading level, and set heading level"
</code></code></pre><p>Note that the prompt is short and general. I wanted to see how well GPT could do without feeding in any additional details or domain-specific knowledge beyond what was in the training set.</p><h2>Output</h2><p>After a few minutes spent generating code, <code>smol-ai</code> created the following files:</p><pre><code><code>.
&#9500;&#9472;&#9472; extension
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; package.json
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; src
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; commands
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; decreaseHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; increaseHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; setHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; extension.ts
&#9492;&#9472;&#9472; shared_dependencies.md
</code></code></pre><p>Note that <code>shared_dependencies.md</code> is created as a best guess of what common dependencies will be. In our case, it created a file with the following contents:</p><pre><code><code>the app is: a vscode extension that lets the user adjust the heading level of the selected text. it should have three commands: increase heading level, decrease heading level, and set heading level

the files we have decided to generate are: tsconfig.json, package.json, utils.ts, extension.ts

Shared dependencies:

1. Exported variables:
   - adjustHeadingLevel (from utils.ts)

2. Data schemas:
   - None

3. ID names of DOM elements:
   - None

4. Message names:
   - None

5. Function names:
   - increaseHeadingLevel
   - decreaseHeadingLevel
   - setHeadingLevel

6. Dependencies:
    - None
</code></code></pre><h2>Code</h2><p><code>extension.ts</code> is the entry point of a vscode extension. In it, we register the three commands that we specified in the prompt.</p><pre><code><code>import * as vscode from 'vscode';
import { increaseHeadingLevel } from './commands/increaseHeadingLevel';
import { decreaseHeadingLevel } from './commands/decreaseHeadingLevel';
import { setHeadingLevel } from './commands/setHeadingLevel';

export function activate(context: vscode.ExtensionContext) {
    context.subscriptions.push(
        vscode.commands.registerCommand('extension.increaseHeadingLevel', increaseHeadingLevel),
        vscode.commands.registerCommand('extension.decreaseHeadingLevel', decreaseHeadingLevel),
        vscode.commands.registerCommand('extension.setHeadingLevel', setHeadingLevel)
    );
}

export function deactivate() {}
</code></code></pre><p>Below is the logic inside of <code>decreaseHeadingLevel.ts</code></p><pre><code><code>import * as vscode from 'vscode';

export async function decreaseHeadingLevel() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        return;
    }

    const document = editor.document;
    const selection = editor.selection;
    const selectedText = document.getText(selection);

    const headingRegex = /^(#+)\s(.*)$/;
    const match = selectedText.match(headingRegex);

    if (!match) {
        vscode.window.showErrorMessage('Invalid selection. Please select a heading to decrease its level.');
        return;
    }

    const currentLevel = match[1].length;
    if (currentLevel === 1) {
        vscode.window.showErrorMessage('Cannot decrease heading level. Already at the lowest level.');
        return;
    }

    const newLevel = currentLevel - 1;
    const newHeading = '#'.repeat(newLevel) + ' ' + match[2];

    await editor.edit((editBuilder) =&gt; {
        editBuilder.replace(selection, newHeading);
    });
}
</code></code></pre><p>The code checks for an active editor and if one exists, gets the selected text and looks for a markdown heading via regex. If a header is found and the current header level isn't already at the lowest level, it decreases the heading level.</p><p>At first glance, there is nothing amiss with the logic. It executes the command and checks for edge cases. It even provides helpful error messages which already puts it ahead of most human-generated programs...</p><h2>Testing the extension</h2><p>To test this extension, we need to successfully execute the following steps:</p><ol><li><p>Install Dependencies</p></li><li><p>Compile Code</p></li><li><p>Run Extension</p></li></ol><h3>Step 1 - Install</h3><p>We run into our first issue when trying to install dependencies.</p><pre><code><code>$ yarn

Couldn't find any versions for "vscode-test" that matches "^1.6.2"
? Please choose a version of "vscode-test" from this list: (Use arrow keys)
&#10095; 1.6.1
</code></code></pre><h3>Issue 1 - Couldn't find vscode-test</h3><p>An inspection of <code>package.json</code> returns the following</p><pre><code><code>{
  "name": "adjust-heading-level",
  ...
  "engines": {
    "vscode": "^1.62.0"
  },
  "devDependencies": {
    "@types/node": "^14.17.0",
    "@types/vscode": "^1.62.0",
    "typescript": "^4.4.2",
    "vscode": "^1.1.37",
    "vscode-test": "^1.6.2"
  },
}
</code></code></pre><p>The vscode engine determines the minimum version of vscode. The present-day (as of 2023-05-23) engine version is <code>1.78</code>. The <code>1.62.0</code> version was <a href="https://code.visualstudio.com/updates/v1_62">released on October 21st, 2021</a></p><p>This corresponds to GPT4's <a href="https://openai.com/research/gpt-4">knowledge cutoff date</a>:</p><blockquote><p>GPT-4 generally lacks knowledge of events that have occurred after the vast majority of its data cut off (September 2021)</p></blockquote><p>The <code>vscode-test</code> version of <code>1.6.2</code> seems suspiciously similar to <code>1.62</code> which indicates that GPT likely hallucinated the numbers.</p><p>In any case, this is easy enough to fix by specifying the correct version number and re-installing</p><pre><code><code>-   "vscode-test": "^1.6.2"
+   "vscode-test": "^1.6.1"
</code></code></pre><p>Re-running the install process is successful the second time around.</p><pre><code><code>$ yarn

...
[3/5] &#128666;  Fetching packages...
[4/5] &#128279;  Linking dependencies...
[5/5] &#128296;  Building fresh packages...
&#10024;  Done in 4.31s.
</code></code></pre><h3>Step 2 - Build</h3><p>Because typescript is a compiled language, we need to execute a build step to compile the code to javascript. The <code>package.json</code> comes with the following scripts:</p><pre><code><code>"scripts": {
    "vscode:prepublish": "npm run compile",
    "compile": "tsc -p ./",
    "watch": "tsc -watch -p ./",
    "postinstall": "node ./node_modules/vscode/bin/install",
    "test": "npm run compile &amp;&amp; node ./node_modules/vscode/bin/test"
  },
</code></code></pre><p>We can build the code by running the <code>compile</code> script. This is where we run into our next issue:</p><pre><code><code>$ yarn compile
warning package.json: No license field
warning adjust-heading-level@0.1.0: The engine "vscode" appears to be invalid.
$ tsc -p ./
error TS5057: Cannot find a tsconfig.json file at the specified directory: './'.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
</code></code></pre><h3>Issue 2 - Cannot find a tsconfig.json</h3><p>Typescript requires a <code>tsconfig.json</code> file to compile into javascript. If you remember our initial file layout, there is no <code>tsconfig.json</code> file.</p><pre><code><code>.
&#9500;&#9472;&#9472; extension
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; package.json
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; src
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; commands
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; decreaseHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; increaseHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; setHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; extension.ts
&#9492;&#9472;&#9472; shared_dependencies.md
</code></code></pre><p>We can remediate this by adding the config and re-building. But now we run into more issues:</p><pre><code><code>$ tsc --init
$ yarn compile

src/commands/decreaseHeadingLevel.ts:1:25 - error TS2307: Cannot find module 'vscode' or its corresponding type declarations.

1 import * as vscode from 'vscode';
                          ~~~~~~~~

src/commands/decreaseHeadingLevel.ts:30:24 - error TS7006: Parameter 'editBuilder' implicitly has an 'any' type.

30     await editor.edit((editBuilder) =&gt; {
                          ~~~~~~~~~~~

src/commands/increaseHeadingLevel.ts:1:25 - error TS2307: Cannot find module 'vscode' or its corresponding type declarations.

...

Found 7 errors
</code></code></pre><h3>Issue 3 - Cannot find modules</h3><p>The reason typescript can't find the module <code>vscode</code> is because of the syntax we use for import statements:</p><pre><code><code>// this is failing
import * as vscode from 'vscode';

// this would work
import vscode from 'vscode';
</code></code></pre><p>The reason for the different syntax comes from the <a href="https://blog.logrocket.com/commonjs-vs-es-modules-node-js/">differences</a> between <code>CommonJs</code> and <code>ES Modules</code> and how they export dependencies as well as how typescript transpiles those exports. The <s>maddening hellscape</s> quirks in module compatibility can be a blog post unto itself - for now, we can fix the issue by disabling <code>esModuleInterop</code> inside of <code>tsconfig.json</code></p><pre><code><code>@@ -71,7 +71,7 @@
-    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+    "esModuleInterop": false,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
</code></code></pre><p>Note that <code>esModuleInterop</code> was changed to <code>true</code> by default since typescript 4.4. This was released on March 15, 2022 - after GPT4's knowledge cutoff date.</p><p>Let's try building again. Just one more error this time:</p><pre><code><code>$ yarn compile

src/commands/setHeadingLevel.ts:2:10 - error TS2305: Module '"../extension"' has no exported member 'adjustHeadingLevel'.

2 import { adjustHeadingLevel } from '../extension';

Found 1 error.
</code></code></pre><h3>Issue 4 - No exported member</h3><p>This last error comes from trying to import a function that does not exist.</p><p>Specifically, the following logic in <code>setHeadingLevel.ts</code>:</p><pre><code><code>import * as vscode from 'vscode';
import { adjustHeadingLevel } from '../extension';

export async function setHeadingLevel() {
  ...
}
</code></code></pre><p>GPT is prone to <s>lie</s> be optimistic about declaring its dependencies. It sometimes calls or imports functions that do not exist. This is one of those cases.</p><p>We can fix this by removing the dependency and manually adding the logic inside of <code>setHeadingLevel</code></p><pre><code><code>@@ -1,5 +1,4 @@
 import * as vscode from 'vscode';
-import { adjustHeadingLevel } from '../extension';
 
 export async function setHeadingLevel() {
     const editor = vscode.window.activeTextEditor;
@@ -14,6 +13,12 @@ export async function setHeadingLevel() {
         vscode.window.showErrorMessage('invalidSelection');
         return;
     }
+    const headingRegex = /^(#+)\s(.*)$/;
+    const match = selectedText.match(headingRegex);
+    if (!match) {
+        vscode.window.showErrorMessage('Invalid selection.');
+        return;
+    }
 
     const inputOptions: vscode.InputBoxOptions = {
         prompt: 'setHeadingLevelPrompt',
@@ -31,6 +36,16 @@ export async function setHeadingLevel() {
 
     if (headingLevel) {
         const newHeadingLevel = parseInt(headingLevel);
-        adjustHeadingLevel(editor, selection, selectedText, newHeadingLevel);
+    
+        const newHeading = '#'.repeat(newHeadingLevel) + ' ' + match[2];
+    
+        await editor.edit((editBuilder) =&gt; {
+            editBuilder.replace(selection, newHeading);
+        });
     }
 }
</code></code></pre><p>Note that most of the code was lifted from <code>decreaseHeadingLevel.ts</code>.</p><p>Lets build again. This time, it's successful &#127881;</p><pre><code><code>$ tsc -p ./
&#10024;  Done in 0.80s.
</code></code></pre><p>But does it run?</p><h3>Step 3 - Run</h3><p>Note that GPT provided no instructions on how to run the extension. Or on how to install or build the extension for that matter. It is simple enough to do if you've built vscode extensions before but this can be a barrier to entry for newcomers.</p><p>Running a vscode extension requires that you go to the "Run and Debug" panel and launch the <code>vscode extension</code> task when the <code>extension.ts</code> file is active in the editor.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hEip!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hEip!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 424w, https://substackcdn.com/image/fetch/$s_!hEip!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 848w, https://substackcdn.com/image/fetch/$s_!hEip!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!hEip!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hEip!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg" width="1456" height="924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Run VSCode Extension&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Run VSCode Extension" title="Run VSCode Extension" srcset="https://substackcdn.com/image/fetch/$s_!hEip!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 424w, https://substackcdn.com/image/fetch/$s_!hEip!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 848w, https://substackcdn.com/image/fetch/$s_!hEip!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!hEip!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf93d08c-586b-448f-ac3e-a23b46347c12_2918x1852.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This launches a new vscode window with our extension installed. This also errors out as soon as I tried invoking a command.</p><pre><code><code>Command 'Increase Heading Level' resulted in an error command 'adjust-heading-level. 'increaseHeadingLevel' was not found
</code></code></pre><h3>Issue 5 - Commands not found</h3><p>VSCode knows about commands when they are declared inside of <code>package.json</code>.</p><p>Our <code>package.json</code> declares the following commands:</p><pre><code><code>  "activationEvents": [
    "onCommand:adjust-heading-level.increaseHeadingLevel",
    "onCommand:adjust-heading-level.decreaseHeadingLevel",
    "onCommand:adjust-heading-level.setHeadingLevel"
  ],
  ...
  "contributes": {
    "commands": [
      {
        "command": "adjust-heading-level.increaseHeadingLevel",
        "title": "Increase Heading Level"
      },
      {
        "command": "adjust-heading-level.decreaseHeadingLevel",
        "title": "Decrease Heading Level"
      },
      {
        "command": "adjust-heading-level.setHeadingLevel",
        "title": "Set Heading Level"
      }
    ]
  }
</code></code></pre><p>After declaring inside of <code>package.json</code>, these commands also need to be registered inside the extension.</p><p>Our <code>extension.ts</code></p><pre><code><code>export function activate(context: vscode.ExtensionContext) {
    context.subscriptions.push(
        vscode.commands.registerCommand('extension.increaseHeadingLevel', increaseHeadingLevel),
        vscode.commands.registerCommand('extension.decreaseHeadingLevel', decreaseHeadingLevel),
        vscode.commands.registerCommand('extension.setHeadingLevel', setHeadingLevel)
    );
}
</code></code></pre><p>Do you see the issue?</p><p>The typescript file declares commands as <code>extension.{COMMAND}</code> but the <code>package.json</code> declares them as <code>adjust-heading-level.{COMMAND}</code></p><p>We can fix this by adjusting <code>package.json</code> to match the code. While the fix itself is simple, being able to diagnose the issue correctly takes some domain knowledge in knowing where to look.</p><pre><code><code>@@ -1,5 +1,5 @@
 {
   "displayName": "Adjust Heading Level",
   "description": "A VSCode extension that lets the user adjust the heading level of the selected text.",
   "version": "0.1.0",
@@ -10,23 +10,20 @@
     "Other"
   ],
   "activationEvents": [
-    "onCommand:adjust-heading-level.increaseHeadingLevel",
-    "onCommand:adjust-heading-level.decreaseHeadingLevel",
-    "onCommand:adjust-heading-level.setHeadingLevel"
   ],
   "main": "./src/extension.js",
   "contributes": {
     "commands": [
       {
-        "command": "adjust-heading-level.increaseHeadingLevel",
+        "command": "extension.increaseHeadingLevel",
         "title": "Increase Heading Level"
       },
       {
-        "command": "adjust-heading-level.decreaseHeadingLevel",
+        "command": "extension.decreaseHeadingLevel",
         "title": "Decrease Heading Level"
       },
       {
-        "command": "adjust-heading-level.setHeadingLevel",
+        "command": "extension.setHeadingLevel",
         "title": "Set Heading Level"
       }
     ]
</code></code></pre><blockquote><p>NOTE: I also used this to remove the <code>activationEvents</code> - these determine when a vscode extension trigger activates. For command-based activations, vscode is now able to detect them automatically and so no longer needs to be manually declared</p></blockquote><p>Let's try running again and increasing the header level.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OV3q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OV3q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!OV3q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!OV3q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!OV3q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OV3q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif" width="1456" height="940" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:940,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:272931,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OV3q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!OV3q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!OV3q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!OV3q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ed37224-9ceb-4154-81c1-9935c97ab6fa_1672x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Well, that's not supposed to happen &#128543;</p><h3>Issue 6 - The Decreasing Increase</h3><p>Instead of increasing the header, we are getting the header level decreased.</p><p>Let's take a look at <code>increaseHeadingLevel.ts</code></p><pre><code><code>import * as vscode from 'vscode';

export async function increaseHeadingLevel() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        return;
    }

    const document = editor.document;
    const selection = editor.selection;
    const selectedText = document.getText(selection);

    const headingRegex = /^(#+)\s(.*)$/;
    const match = selectedText.match(headingRegex);

    if (!match) {
        vscode.window.showErrorMessage('Invalid selection. Please select a valid heading.');
        return;
    }

    const currentLevel = match[1].length;
    const newLevel = Math.max(1, currentLevel - 1);
    const newText = '#'.repeat(newLevel) + ' ' + match[2];

    await editor.edit((editBuilder) =&gt; {
        editBuilder.replace(selection, newText);
    });
}
</code></code></pre><p>Do you see the issue?</p><p>There is a bug caused by a single character diff.</p><pre><code><code>@@ -19,7 +19,7 @@ export async function increaseHeadingLevel() {
     }
 
     const currentLevel = match[1].length;
-    const newLevel = Math.max(1, currentLevel - 1);
+    const newLevel = Math.max(1, currentLevel + 1);
     const newText = '#'.repeat(newLevel) + ' ' + match[2];
</code></code></pre><p>Let's compile and run it again.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FziN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FziN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!FziN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!FziN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!FziN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FziN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif" width="1456" height="940" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:940,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1142319,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FziN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 424w, https://substackcdn.com/image/fetch/$s_!FziN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 848w, https://substackcdn.com/image/fetch/$s_!FziN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 1272w, https://substackcdn.com/image/fetch/$s_!FziN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cd3cf23-41b6-4400-876d-aad36a69ab6a_1672x1080.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>It works &#129395;</p><h2>Thoughts</h2><p>So how did we do? We got a working extension. We got it to accomplish the goal set out in our prompt.</p><p>The journey to this point was not "automatic". We ran into many issues along the way. Lacking prior knowledge of typescript, node.js and vscode, these issues would have taken a while to debug.</p><p>And even though the code we were able to generate working code, there are still many improvements to be made:</p><ul><li><p>there are no instructions on how to develop, use, or publish the extension</p></li><li><p>there is no <code>.gitignore</code> for typescript/javascript/vscode artifacts</p></li><li><p>there is no <code>launch.json</code> file that configures running the extension in development</p></li><li><p>there are no tests</p></li><li><p>there is no code reuse</p></li></ul><h2>Some Stats</h2><p>GPT generated 9 files that cover ~100 lines of typescript, ~180 lines json, and 17 lines of markdown.</p><pre><code><code>$ cloc --exclude-dir=node_modules,out --not-match-f=package-lock.json --not-match-f=prompt.md --include-ext=ts,json,md .
      15 text files.
      13 unique files.
       7 files ignored.

github.com/AlDanial/cloc v 1.92  T=0.01 s (986.5 files/s, 36610.4 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
JSON                             4              8              0            181
TypeScript                       4             22              0             98
Markdown                         1              8              0             17
-------------------------------------------------------------------------------
SUM:                             9             38              0            296
-------------------------------------------------------------------------------
</code></code></pre><p>The final file tree</p><pre><code><code>$ tree --gitfile extension/.gitignore
.
&#9500;&#9472;&#9472; extension
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; package.json
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; src
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; commands
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; decreaseHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; increaseHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; setHeadingLevel.ts
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; extension.ts 
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; tsconfig.json
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; yarn.lock
&#9500;&#9472;&#9472; prompt.md
&#9492;&#9472;&#9472; shared_dependencies.md
</code></code></pre><p>Out of the ~300 lines generated, we had to modify/add ~18 lines in order to make everything work.</p><h2>Takeaways</h2><p>GPT was able to generate most of the code using a naive prompt with no domain-specific context.</p><p>Some things to note:</p><ul><li><p>GPT4 does great with code in its index but will likely generate bad logic if the underlying specs have changed since its knowledge cutoff date (September 2021)</p></li><li><p>GPT4 can hallucinate subtle bugs. In the <code>increaseHeadingLevel.ts</code> case, it was a one-character difference that caused the extension to do the exact opposite of what the command was supposed to do</p></li><li><p>GPT4 is great at scaffolding boilerplate but domain expertise still matters (for now). This is especially true when building on the tech that has changed since GPT4's cutoff date</p></li><li><p>GPT4 introduces yet another abstraction layer for programming. We now have 7 translation layers for the case of writing typescript (which can easily be doubled when involving containers or VMs) &#129318;&#8205;&#9794;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lS_t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lS_t!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 424w, https://substackcdn.com/image/fetch/$s_!lS_t!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 848w, https://substackcdn.com/image/fetch/$s_!lS_t!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!lS_t!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lS_t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png" width="1456" height="504" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:504,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:181211,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lS_t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 424w, https://substackcdn.com/image/fetch/$s_!lS_t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 848w, https://substackcdn.com/image/fetch/$s_!lS_t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 1272w, https://substackcdn.com/image/fetch/$s_!lS_t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18829857-0b0e-4aea-9528-605d1b32d071_2940x1018.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Turtles&#8230;</figcaption></figure></div></li></ul><h2>Future Directions</h2><p>I did the initial experiment with a naive general prompt and no additional context. There is lots of room for improvement. Some next steps:</p><ul><li><p>every issue that we encountered when trying to run the extension, include it as a detail in the prompt for GPT to watch out for</p><ul><li><p>generalize this by indexing and summarizing the vscode extension docs to mitigate for new information that is not in GPT's current index</p><ul><li><p>explore doing this by chaining an LLM that has access to today's context (eg. Bard) with GPT</p></li></ul></li></ul></li><li><p>generate tests to validate logic and have GPT autocorrect itself if tests fail</p></li><li><p>generate a checklist for what a high-quality vscode extension looks like and have GPT verify and autocorrect the artifacts it generates</p></li></ul><blockquote><p>NOTE: I've already run a subset of these steps and was able to get the error count to zero on the first generation. Will need to see if it generalizes to other examples. Look out for details in a future post</p></blockquote><h2>A Note to the reader</h2><p>I'm a solo YC founder that is currently pivoting into building useful tools in the LLM space. If this is something that you're passionate about and want to be a part of, either as a cofounder (I'm looking for someone with a technical background in sales/product/go-to-market) or as a founding team member, please reach out.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Using GPT to Compile Pseudocode: A New Approach to Programming]]></title><description><![CDATA[Like everyone on the internet, I've been playing around with large language models (LLMs).]]></description><link>https://bit.kevinslin.com/p/using-gpt-to-translate-pseudocode</link><guid isPermaLink="false">https://bit.kevinslin.com/p/using-gpt-to-translate-pseudocode</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Tue, 16 May 2023 14:09:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Eyms!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Eyms!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Eyms!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!Eyms!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!Eyms!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!Eyms!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Eyms!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1401399,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Eyms!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!Eyms!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!Eyms!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!Eyms!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b4c7a7f-566b-4136-bb15-9359990d7bf8_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Like everyone on the internet, I've been playing around with large language models (LLMs). As a result, it has changed the way I think and write code.</p><p>Today, I'm sharing how I start most of my coding sessions, by using GPT as a compiler to translate pseudocode into real code.</p><p>All examples below are generated using chatgpt-3.5</p><blockquote><p>NOTE: while you generally get better results running chatgpt-4 instead of 3.5, I find the huge latency in responses to not be worth the incremental improvements that chatgpt-4 provides</p></blockquote><h2>What is Pseudocode</h2><blockquote><p>Pseudocode is a plain language description of the steps in an algorithm or another system. Pseudocode often uses structural conventions of a normal programming language but is intended for human reading rather than machine reading. Source: <a href="https://en.wikipedia.org/wiki/Pseudocode">Wikipedia</a></p></blockquote><p>Pseudocode uses <strong>plan language</strong> to describe a particular algorithm to humans. The output is prose that is easily understood by humans but not executable by machines.</p><p>Large language models are trained to take plain language intended for humans and generate effective outputs from the description. Code, aka instructions that can be executed by machines, is one of those outputs.</p><p>So what if we combine them? Let's have our cake and eat it too!</p><h2>Why Pseudocode</h2><p>Normally, when you see examples of GPT-generated code, they come in the form of simple directives like "fetch the top 50 items from hacker news" or "get the stock price for Google".</p><p>While these directives do generate executable code (most of the time), they do not enforce fine-grained constraints. Constraints arise in most real-world applications - maybe you're working with an existing code base that limits what dependencies you can use or you have to accomplish a complicated workflow in a particular sequence.</p><p>Pseudocode works well when in these situations and allows you, the programmer, to have all the benefits of LLMs while still retaining control of the output.</p><h2>Case Study: Fetching All the AWS Docs</h2><p>Today's example goes over the actual workflow I used to create <a href="https://awsnotes.dendron.so/about/readme">AWS Reference Notes</a> - this project attempts to consolidate all known gotchas, limitations and caveats of various AWS services.</p><p>AWS docs are available in public git repositories. As part of the project, I needed to implement logic that could clone these repos into a specific directory layout. I also needed the code to use the <code>isomorphic-git</code> library which allows git to be run both on the web as well as on a server.</p><h3>Prompt 1</h3><p>My initial prompt to gpt.</p><pre><code>Convert the given pseudocode into valid typescript. 

The following third-party libraries will be available:
- isomorphic-git as git

The following operators are natural language operations. Translate these directives into valid typescript. Make sure to implement all logic. 
- `%{action}%`: implement the desired {action} using what you know about the language and available libraries
- `%{library}|{action}%`: implement the desired {action} using the suggested {library}. you MUST use functions available in {library}. if none exist, create a helper function that accomplishes the desired {action}

Pseudocode below:
```ts
export main(services: string[]) {
    %set basedir to `__dirname/..`%
    basedir = 

    await Promise.all services.map( async service =&gt; {
        await upsertDevGuide({service, basedir})
    })
}

awsgitrepo(service: string) {
    return `https://github.com/awsdocs/${service}-developer-guide.git`
}

upsertDevGuide(opts: {service: messtring, basedir: string}) {
    guidePath = path.join(basedir, "docs", service, "developer-guide")
    if !(
        %git|check if "guidePath" exists and is a git repo%

    ) {
        %ensure that the directory for "guidePath" exists, create if it does not%
        %git|run git clone at "guidePath" using output of "awsgitrepo(service)"%

    } else {
        %git|run git pull at "guidePath"%
    }
}
```
</code></pre><p>This prompt has 3 directives:</p><ol><li><p>specify the desired output format (<code>typescript</code>).</p></li><li><p>specify the availability of third-party libraries (<code>isomorphic-git</code>)</p></li><li><p>specify <strong>natural language operations (NLOs)</strong> that we want GPT to translate into code.</p></li></ol><p>There are two NLOs in this example:</p><ul><li><p><code>%{action}%</code>: perform any action (wildcard action NLO)</p></li><li><p><code>%{library}|{action}%</code>: perform an action using the suggested library (library action NLO)</p></li></ul><p>The <code>wildcard nlo</code> is useful when you want a specific side effect or state to happen that requires multiple steps (eg. ensuring a directory exists). The <code>library nlo</code> is useful when you know a library has a particular function and you can't be bothered to fill in all the boilerplate.</p><h3>Response 1</h3><pre><code>Here's the conversion of the given pseudocode into valid TypeScript, taking into account the natural language operations:

```typescript
import * as git from 'isomorphic-git';
import * as path from 'path';

export async function main(services: string[]) {
  const basedir = path.join(__dirname, '..');

  await Promise.all(services.map(async (service) =&gt; {
    await upsertDevGuide({ service, basedir });
  }));
}

function awsgitrepo(service: string) {
  return `https://github.com/awsdocs/${service}-developer-guide.git`;
}

async function upsertDevGuide(opts: { service: string, basedir: string }) {
  const guidePath = path.join(opts.basedir, 'docs', opts.service, 'developer-guide');
  
  const isGitRepo = await git.isGitRepo({ fs, dir: guidePath }).catch(() =&gt; false);

  if (!isGitRepo) {
    await ensureDirectoryExists(guidePath);
    await git.clone({ dir: guidePath, url: awsgitrepo(opts.service) });
  } else {
    await git.pull({ fs, dir: guidePath });
  }
}

async function ensureDirectoryExists(directoryPath: string) {
  const fs = git.plugins.get('fs');
  const dirPath = path.dirname(directoryPath);
  
  // Create directory if it doesn't exist
  await fs.promises.mkdir(dirPath, { recursive: true });
}
```

Please note that in order to use `isomorphic-git` and its functions, you need to have it installed as a dependency in your TypeScript project.
</code></pre><p>The initial response from gpt doesn't compile but gets us most of the way there. It has the following issues:</p><ul><li><p><code>git</code> operations require additional arguments (<code>http</code> and <code>fs</code> parameters)</p></li><li><p><code>git.isGitRepo</code> is a hallucination and not a real function</p></li><li><p><code>ensureDirectoryExists</code> uses <code>git.plugins.get('fs')</code> (which might be hallucinated) instead of the builtin <code>fs</code> module</p></li></ul><h3>Prompt 2</h3><p>When first encountering issues, it's worthwhile to ask GPT to make corrections.</p><pre><code><code>isGitRepo isn't a function
</code></code></pre><p>Note that the follow-up is much more concise than the initial prompt. GPT already has the prior context so only minimal prompting is needed to "fine-tune" the results.</p><h3>Response 2</h3><pre><code>Apologies for the confusion. You're correct that `isGitRepo` is not a built-in function in `isomorphic-git`. Instead, we can check if the `.git` directory exists in the specified path to determine if it's a git repository. Here's the updated code:

```typescript
import * as git from 'isomorphic-git';
import * as fs from 'fs';
import * as path from 'path';

export async function main(services: string[]) {
  const basedir = path.join(__dirname, '..');

  await Promise.all(services.map(async (service) =&gt; {
    await upsertDevGuide({ service, basedir });
  }));
}

function awsgitrepo(service: string) {
  return `https://github.com/awsdocs/${service}-developer-guide.git`;
}

async function upsertDevGuide(opts: { service: string, basedir: string }) {
  const guidePath = path.join(opts.basedir, 'docs', opts.service, 'developer-guide');
  
  const isGitRepo = await checkIfGitRepo(guidePath);

  if (!isGitRepo) {
    await ensureDirectoryExists(guidePath);
    await git.clone({ dir: guidePath, url: awsgitrepo(opts.service) });
  } else {
    await git.pull({ dir: guidePath });
  }
}

async function checkIfGitRepo(directoryPath: string): Promise {
  const gitDirPath = path.join(directoryPath, '.git');

  return new Promise((resolve) =&gt; {
    fs.access(gitDirPath, fs.constants.F_OK, (err) =&gt; {
      resolve(!err);
    });
  });
}

async function ensureDirectoryExists(directoryPath: string) {
  const dirPath = path.dirname(directoryPath);

  // Create directory if it doesn't exist
  await fs.promises.mkdir(dirPath, { recursive: true });
}
```

This updated code checks if the `.git` directory exists in the specified path to determine if it's a git repository. If the `.git` directory exists, it is considered a git repository; otherwise, it is not.
</code></pre><p>Since GPT generates the entire code with the corrections, it can be hard to spot the changes. I've highlighted them in the diff below.</p><pre><code><code>diff --git a/src/pseudocode.gpt35.1.ts b/src/pseudocode.gpt35.1.ts
index 6977cae..425cc2a 100644
--- a/src/pseudocode.gpt35.1.ts
+++ b/src/pseudocode.gpt35.1.ts
@@ -1,4 +1,5 @@
 import * as git from 'isomorphic-git';
+import * as fs from 'fs';
 import * as path from 'path';
 
 export async function main(services: string[]) {
@@ -16,20 +17,29 @@ function awsgitrepo(service: string) {
 async function upsertDevGuide(opts: { service: string, basedir: string }) {
   const guidePath = path.join(opts.basedir, 'docs', opts.service, 'developer-guide');
   
-  const isGitRepo = await git.isGitRepo({ fs, dir: guidePath }).catch(() =&gt; false);
+  const isGitRepo = await checkIfGitRepo(guidePath);
 
   if (!isGitRepo) {
     await ensureDirectoryExists(guidePath);
     await git.clone({ dir: guidePath, url: awsgitrepo(opts.service) });
   } else {
-    await git.pull({ fs, dir: guidePath });
+    await git.pull({ dir: guidePath });
   }
 }
 
+async function checkIfGitRepo(directoryPath: string): Promise&lt;boolean&gt; {
+  const gitDirPath = path.join(directoryPath, '.git');
+
+  return new Promise&lt;boolean&gt;((resolve) =&gt; {
+    fs.access(gitDirPath, fs.constants.F_OK, (err) =&gt; {
+      resolve(!err);
+    });
+  });
+}
+
 async function ensureDirectoryExists(directoryPath: string) {
-  const fs = git.plugins.get('fs');
   const dirPath = path.dirname(directoryPath);
-  
+
   // Create directory if it doesn't exist
   await fs.promises.mkdir(dirPath, { recursive: true });
 }
</code></code></pre><p>Note that GPT was able to fix the issue with the hallucinated <code>git.isGitRepo</code> function. In the process, it also simplifies the <code>ensureDirectoryExists</code> and switched to the node <code>fs</code> dependency. That said, it also went one step back by removing the (required) <code>fs</code> parameter from the <code>git.pull</code> function.</p><h3>Finishing it up.</h3><p>With more prompting, we can get GPT to fix the remaining issues with the code. I find that this isn't worthwhile as it becomes simpler to manually apply the remaining fixes at this point.</p><pre><code><code>index 425cc2a..3bff3d9 100644
--- a/src/pseudocode.gpt35.1.ts
+++ b/src/pseudocode.gpt35.1.ts
@@ -1,6 +1,7 @@
 import * as git from 'isomorphic-git';
 import * as fs from 'fs';
 import * as path from 'path';
+import http from 'isomorphic-git/http/node';
 
 export async function main(services: string[]) {
   const basedir = path.join(__dirname, '..');
@@ -21,9 +22,9 @@ async function upsertDevGuide(opts: { service: string, basedir: string }) {
 
   if (!isGitRepo) {
     await ensureDirectoryExists(guidePath);
-    await git.clone({ dir: guidePath, url: awsgitrepo(opts.service) });
+    await git.clone({ dir: guidePath, url: awsgitrepo(opts.service), fs, http });
   } else {
-    await git.pull({ dir: guidePath });
+    await git.pull({ fs, dir: guidePath, http });
   }
 }
</code></code></pre><p>As you can see, only 3 lines needed to be changed to arrive at the working code.</p><h2>Next Steps</h2><p>There's a lot more to talk about that didn't make it in this article. Some things I'm currently experimenting with:</p><ul><li><p>enforcing high-level styles (eg. prefer object-based arguments vs positional arguments)</p></li><li><p>additional operators (eg. <code>A := B</code>: extract anywhere from object operator. This extracts property "A" from "B". This requires inferring a path that will yield the value "A" from "B")</p></li><li><p>automatically loading in additional context from the existing codebase</p></li></ul><p>There's no end to things that you can do here - context size's the limit. If you have interesting examples of using GPT for code, please share them either in the comments below or reach out at kevin(a t)dendron.so. I'm currently thinking about putting together a repo to collect examples of how people code using GPT and looking for folks that are interested in contributing &#127793;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[A Question is Worth a Thousand Answers]]></title><description><![CDATA[An answer is a dead thing.]]></description><link>https://bit.kevinslin.com/p/a-question-is-worth-a-thousand-answers</link><guid isPermaLink="false">https://bit.kevinslin.com/p/a-question-is-worth-a-thousand-answers</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Mon, 01 May 2023 14:00:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FcXH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FcXH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FcXH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!FcXH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!FcXH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!FcXH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FcXH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1361868,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FcXH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!FcXH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!FcXH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!FcXH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40304555-f255-4556-97e9-e7c7c85a9532_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>An answer is a dead thing. A snapshot at a point in time.</p><p>Yes. No. Maybe. </p><p>I agree. I disagree. </p><p>This is good. This is bad.</p><p>Answers close of possibilities. The collapse infinity into a singularity. When people hold on to answers, it becomes dogma.</p><p>What are questions?</p><p>A question is a moving target. An invitation to be curious.</p><p>What? </p><p>Why? </p><p>How? </p><p>When?</p><p>Questions represent possibilities. They ask you to take in what is and imagine what could be. When people consider a question, they embark on a journey.</p><p>So ask yourself.</p><p>Who are you? </p><p>What do you stand for? </p><p>What do you yearn to do? </p><p>When will you start?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Letter to a Burned Out Founder]]></title><description><![CDATA[Dear Founder,]]></description><link>https://bit.kevinslin.com/p/letter-to-a-burnt-out-founder</link><guid isPermaLink="false">https://bit.kevinslin.com/p/letter-to-a-burnt-out-founder</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Sun, 23 Apr 2023 14:19:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CIrt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CIrt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CIrt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!CIrt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!CIrt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!CIrt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CIrt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:492244,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CIrt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!CIrt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!CIrt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!CIrt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf427ada-a812-4f4e-b725-ee26bdb632ee_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Dear Founder,</p><p>Burnout sucks. It is an insidious parasite that festers underneath the surface. You might not even notice it growing until the day you wake up and realize that you are underwater.</p><p>Burnout robs you of your joy, your will, and your abilities to do your highest calling.</p><p>And even if you are aware of both the fact and the consequence - more likely than not, you will continue and push forward. Because to do otherwise would mean acknowledging that you are burned out. And that weight is too much to bear. So you put off the day of reckoning and continue working. But the burnout is still there. Collecting interest. And it will come due.</p><h2>The Two Phases of a (Venture-Backed) Startup</h2><p>Burnout often happens in the "early" part of the startup journey.</p><p>This part is marked by two distinct phases:</p><ul><li><p>the phase before <a href="https://www.kevinslin.com/notes/htchyy536bb169mzu47ksb6/">product market fit</a> (PMF)</p></li><li><p>the phase after PMF</p></li></ul><p>The action of pushing a boulder up a hill is used to describe the phase before PMF. It is grueling work and you're not quite sure who will win in the end - you or the boulder.</p><p>The action of chasing a boulder down a hill is used to describe the phase after PMF. This is a different but equally hard sort of work as you run as fast as you can just to keep up with boulder.</p><p>This letter deals with burnout that can occur pushing the boulder up the hill.</p><h2>The Burnout</h2><p>I came across a <a href="https://news.ycombinator.com/item?id=35577712">recent hacker news comment</a> that described burnout as an inequality between "resources", "agency", and "care".</p><p>Resources describe what you have at your disposal to achieve a particular outcome - this usually is some combination of personal capabilities, capital, and team. </p><p>Agency describes how much control you have over the outcome. </p><p>Care describes how invested you are in the outcome.</p><p>In big tech, the regular worker has access to near-unlimited resources but often very little agency. In a startup, the opposite is true.</p><p>People can burn out in big tech because they care about the outcome but have little agency in controlling it. In startups, founders burn out because they care about the outcome but run out of the resources necessary to achieve it.</p><p>As a startup founder, you make up for the lack of resources by being agile. By doubling down in a few key areas and executing extremely well. By putting in sweat, blood, and tears to push your idea forward.</p><p>In the phase before PMF, all your effort is concentrated on pushing the boulder up the hill.</p><h2>When Push Leads to Burnout</h2><p>Burnout happens when you push the boulder up the hill for too long with little progress and no end in sight.</p><p>Maybe you're pushing the wrong boulder (your boulder is made out of lead). Maybe you're on the wrong hill (consider going up the one that isn't covered in snow). Whatever the reason - you come to realize that this boulder is not going anywhere.</p><p>Meanwhile, you've already spent years (and capital) working on this boulder. Your "resources" are running low and prospects are drying up. What do you do?</p><p>This depends on your mental model of "doing a startup".</p><h2>Striking Gold</h2><p>One mental model of doing startups is that of "looking for gold".</p><p>Imagine a prospector searching for gold. He or she will continue forward because success comes with finding the gold. There's always the chance that the next dig will lead to the mother lode and they can exhaust all options to get to that outcome.</p><p>In startup lingo, replace the dig with the next feature launch. The next enterprise customer. The next influencer campaign.</p><p>In the "looking for gold" model, you are working for a singular outcome that will make you whole. Once you achieve it, all your problems go away.</p><p>This isn't the case for startups. Say you do "find gold" and make it to the top of the hill. Your journey is not done. Finding PMF means that it is just beginning. Now the hard work of chasing that boulder down the hill begins.</p><p>The average SaaS company <a href="https://about.crunchbase.com/blog/startup-exit/">takes 9 years to exit</a>. Even if you think you can push that boulder to the top by sheer force of will, can you continue for another five to ten years if you've already burned yourself out getting there?</p><h2>Infinite Games</h2><p>Another mental model of doing startups is that of an "infinite game".</p><p>The concept of an infinite game is discussed in the book <a href="https://amzn.to/43Txs0h">Finite and Infinite Games</a>. A finite game has an end and players play to end the game. An infinite game has no end, players play to continue the game.</p><p>While venture-backed startups can seem like a finite game with a set end (aka exit) in mind, it's not helpful to think of them as such.</p><p>This is because most startups fail - if you are trying to measure success by the outcome of any one startup, it is like a gambler going all in on a single round of roulette.</p><p>If you do your startup with this mindset, you will continue pushing that boulder even if it is made out of lead. Regardless of whether you make it past the hill, you will probably burn through all your resources getting there, causing the "game" to end.</p><p>By "game", I'm not talking about the current manifestation of your startup. Rather, I'm talking about your spirit. Your passion. Your ability to continue playing the game.</p><p>Your odds of "winning" the game become much higher if you play the "infinite game". In doing so, you detach yourself from the outcome and invest in the journey. It means being willing to fail, learn from mistakes, and start over if need be. It means taking care of your "resources" because you are playing the "infinite game".</p><h2>Taking a break</h2><p>If you identify yourself as someone that has pushed a lead boulder up a snowcapped mountain for much too long, you have a few options.</p><p>You can keep pushing and hope you strike gold soon. And hope that in your current state, you can last another five to ten years chasing that boulder.</p><p>Or you can choose to step away. Take a break. This means walking away from your startup for some time. A week. A month. Maybe even a year. Whatever you have to do to restock your resources and rekindle the original excitement that got you here in the first place.</p><p>Of course, this is much easier said. This comes as naturally as eating nails. It feels like giving up. Of letting down everyone - your customers, your team, and your investors.</p><p>But take the other side of this argument. Say you continue pushing forward. The success of a startup is determined, in a large part, by the team behind it. The earlier and smaller the startup, the more of an outsized impact the founder will have in that team and hence the startup. Given the dismal odds of most startups succeeding, how likely will it be for your startup to do so if you're already burned out?</p><p>Continuing to push forward at this point is like the airport making people take off their shoes in the security line - playing along for appearance's sake vs actually addressing the real issues.</p><p>Sometimes the only way forward is to step back.</p><p>Are you burned out? If so, consider taking a break. The game will still be there when you get back. As long as you are still willing to play.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[My Other Computer is Amazon EC2]]></title><description><![CDATA[I used to have a sticker on my laptop that said "My other computer is Amazon EC2".]]></description><link>https://bit.kevinslin.com/p/my-other-computer-is-amazon-ec2</link><guid isPermaLink="false">https://bit.kevinslin.com/p/my-other-computer-is-amazon-ec2</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Tue, 18 Apr 2023 14:01:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!UZK0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UZK0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UZK0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!UZK0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!UZK0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!UZK0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UZK0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1732827,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UZK0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!UZK0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!UZK0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!UZK0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe92b6ea8-9cc0-496d-bb0d-d67fdc8bf055_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I used to have a sticker on my laptop that said "My other computer is Amazon EC2". This was true in the sense that I had many EC2 instances running at any given time doing compute on data and shuffling packets around the web. Amazon EC2 was not, however, a platform I used for day-to-day development.</p><p>My daily driver was a 13" Macbook. This worked well until it didn't. It started not working so well when memory became a bottleneck - this usually happened when I did anything with docker or had too many tabs open on Firefox.</p><p>After one too many computer panics due to memory issues, I invested in making EC2 my primary development environment. This transition happened around 2015 and I have been running on it ever since.</p><p>Running your dev environment on the cloud is a paradigm that has only become more versatile over time, improving from better instance types (eg. gravitron), development-friendly features (eg. hibernation), and developer tooling (eg. CDK).</p><p>You can find my dev setup on <a href="https://github.com/kevinslin/personal-cloud">github</a> as a CDK template. Executing the template will provision a personal cloud-based dev environment that can be spun up anywhere in the world (where there are AWS data centers).</p><p>Unfortunately, it's the year 2023 and setting this up via IaC (infrastructure as code) still requires some knowledge of AWS plumbing. This post goes over some of those technical details.</p><h2>Architecture</h2><blockquote><p>NOTE: this article assumes you're familiar with the basics of AWS</p></blockquote><p>If you deploy using the <code>personal-cloud</code> template, you'll end up with the following resources:</p><ul><li><p>1 VPC</p></li><li><p>1 Autoscaling Group</p></li><li><p>1 EC2 Instance</p></li></ul><h3>VPC</h3><p>The VPC consists of:</p><ul><li><p>1 public subnet (has inbound and outbound paths to the internet)</p></li><li><p>0 private subnets (has no inbound but outbound to the internet)</p></li><li><p>1 isolated subnet (no path to public internet)</p></li><li><p>0 NAT gateways</p></li></ul><p>Since I'm using this as a dev environment, the EC2 instance is launched in a public subnet. This is because we'll need to pull in packages from various programming languages (eg. npm) as well as download dependencies (eg. docker images) that we don't want to pay extra network fees for via a NAT gateway. The EC2 instance itself is locked down via security group rules that only permit inbound on port 22.</p><p>There are no private subnets as this stack is not meant to run any services besides development ec2 instances. I keep an isolated subnet around in reserve in case I need to spin up additional resources outside of my ec2 instance (eg. a database on a separate ec2 instance).</p><p>Note that the template by default creates a separate VPC. If you are working in a corporate environment, you might already have a development account and an existing VPC to launch your EC2 instance into. That might be preferable as you can then also connect to other internal services from inside the VPC.</p><h3>Autoscaling Group</h3><p>I like to add an autoscaling group with EC2, even if it is just a single EC2 instance. Autoscaling by itself does not cost anything and gives us health checks for free. If I do end up needing multiples of the same development instance, it will also be easy to provision by toggling the <code>min-capacity</code> threshold.</p><h3>EC2 Instance</h3><p>The EC2 instance has a custom security group that by default, only allows traffic from port 22 (ssh). You can optionally add additional inbound depending on your use case (eg. testing a WebSocket endpoint).</p><p>I generally find that a lot of work I do is either memory-bound or CPU bound. Depending on the case, I will provision a <code>r6a</code> or <code>c6a</code> type of ec2 instance.</p><p>To decode the instance family names:</p><ul><li><p>R: memory-optimized</p></li><li><p>C: compute optimized</p></li><li><p>A: AMD based (generally cheaper than i[ntel] based architectures)</p></li></ul><blockquote><p>NOTE: in most cases, you can also use arm-based <a href="https://aws.amazon.com/ec2/graviton/">gravitron</a> instances (eg. swap out <code>r6a</code> with <code>r6g</code>) to deliver slightly better performance at less cost. Since most of my development work still targets non-arm architectures, I like to keep my environment the same as that of my target environments. If you end up using gravitron, you can use the newest generation <code>c7g</code> and <code>r7g</code> instances which offer the best "compute/memory to price-performance ratio" on AWS</p></blockquote><p>The operating system is Amazon Linux 2. Note that this does not have many of the development tools you might be used to by default in other Linux distributions (eg. git). It is a secure Linux distro that is tuned to run with AWS and has long-term support.</p><p>I generally add some common user data scripts (a set of commands that run at the startup of a cloud VM) that add some developer tools like <code>git</code> and <code>docker</code>.</p><p>Note that by default, EBS-backed AWS instances will launch with 8GB of disk space. The template changes the default to 100GB.</p><h2>Etc</h2><p>The architecture here represents the basics of getting something running in AWS. There are many additional things you can do to optimize in terms of cost and DX.</p><p>Some of my favorites which are not covered in this post:</p><ul><li><p>adding <a href="https://tailscale.com/kb/1052/install-amazon-linux-2/">tailscale</a> to ec2 so you can use it to create your personal VPC of self-hosted things</p></li><li><p>hibernating ec2 instance when not in use (and creating a script to automatically do this)</p></li><li><p>connecting to your ec2 instance via vscode</p></li><li><p>using reserved instances to further reduce costs by up to 75%</p></li></ul><h2>Conclusion</h2><p>Today, it would be more accurate to simply say that "my computer is Amazon EC2". AWS gives you infinite freedom in creating a custom dev environment tailored for anything you might need. With this freedom also comes great complexity and hidden foot guns. The goal of this post is to give you a starting point for creating your personal cloud. Enjoy responsibly and watch out for dragons.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Why I switched to Arc as my default browser]]></title><description><![CDATA[For as long as I can remember, I've had a polyamorous relationship with web browsers.]]></description><link>https://bit.kevinslin.com/p/why-i-switched-to-arc-as-my-default</link><guid isPermaLink="false">https://bit.kevinslin.com/p/why-i-switched-to-arc-as-my-default</guid><dc:creator><![CDATA[Kevin Lin]]></dc:creator><pubDate>Sun, 02 Apr 2023 15:01:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XSnZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For as long as I can remember, I've had a polyamorous relationship with web browsers. In the past, I've alternated between Chrome and Firefox depending on my <s>RAM</s> needs. Chrome was sleek and fast, but Firefox was the workhorse that let me manage hundreds of nested vertical tabs (via <a href="https://addons.mozilla.org/en-US/firefox/addon/sidebery/">Sidebery</a>) and juggle multiple accounts using <a href="https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/">containers</a>.</p><p>My main gripe with Firefox was its insatiable appetite for memory as it would regularly consume 10GB in the course of a day.</p><p>Enter Arc - a modern take on the web browser that's meant to be the "operating system for the web." Built on top of Chromium and advertised as a reimaging of what the browser could be, I recently got an invite to try it out. After two weeks of using it as my default browser, I'm not going back.</p><h2>Arc</h2><p>The first thing you'll notice about Arc is the sidebar. It's a jack-of-all-trades that functions as the address bar, menu, bookmarks, and an entire drawer full of additional functionality.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XSnZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XSnZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 424w, https://substackcdn.com/image/fetch/$s_!XSnZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 848w, https://substackcdn.com/image/fetch/$s_!XSnZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 1272w, https://substackcdn.com/image/fetch/$s_!XSnZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XSnZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png" width="1456" height="941" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:941,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1347231,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XSnZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 424w, https://substackcdn.com/image/fetch/$s_!XSnZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 848w, https://substackcdn.com/image/fetch/$s_!XSnZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 1272w, https://substackcdn.com/image/fetch/$s_!XSnZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92d00723-1aa6-4bb8-b3df-de77de437bf6_2000x1293.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The sidebar itself exists as part of a "space" - a preconfigured set of tabs that is attached to a profile. Profile are like Firefox containers - they let you isolate online identities by compartmentalizing cookies and browser storage between different profiles.</p><p>With profiles, spaces, and tabs, Arc gives me the power features I needed from Firefox with the affordances of Chrome. It's compatible with all Chrome extensions (at least the ones I use), and its developer tools function as you'd expect from a Chromium browser. Additionally, Arc has many awesome quality-of-life features not found in other browsers, like the ability to do video in video when switching tabs and the ability to peek at links before committing to replacing the current page with it.</p><p>Going beyond the features, Arc is simply delightful to use. It is packed with little flourishes like musical icons that radiate from the tab when sound is playing and vibrant color palettes to make each space feel distinct.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dKj-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dKj-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 424w, https://substackcdn.com/image/fetch/$s_!dKj-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 848w, https://substackcdn.com/image/fetch/$s_!dKj-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 1272w, https://substackcdn.com/image/fetch/$s_!dKj-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dKj-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png" width="1456" height="941" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:941,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6089703,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dKj-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 424w, https://substackcdn.com/image/fetch/$s_!dKj-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 848w, https://substackcdn.com/image/fetch/$s_!dKj-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 1272w, https://substackcdn.com/image/fetch/$s_!dKj-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ef7834b-faf5-4801-8971-1551ffa09359_3456x2234.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Arc works so well that I've switched to using it for most of my Electron-based apps like Slack, Discord, and Notion. This ends up saving memory, but it does mean retraining my fingers to do <code>Alt-Tab</code> instead of <code>Shift-Tab</code> to switch between apps.</p><h2>Parting Thoughts</h2><p>Arc is a delightful browser developed by an amazing team (including one of the <a href="vscode-webview://0m824acapknjiimv6p6oght39a3u0ulagbb3mj2g8cr0lmshsppo/(https://browsercompany.substack.com/p/what-comes-after-chrome)">creators</a> of google chrome). It's become my default browser over the last two weeks and I don't see myself going back.</p><p>The browser space has been in need of fresh air for some time, and Arc delivers just that and more.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://bit.kevinslin.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Bit by bit! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>