<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: honeycomb</title>
    <description>The latest articles on DEV Community by honeycomb (@honeycomb_io).</description>
    <link>https://dev.to/honeycomb_io</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F347046%2F89a9ef7d-b5b7-4c61-b2cc-ae7939a84d9c.jpg</url>
      <title>DEV Community: honeycomb</title>
      <link>https://dev.to/honeycomb_io</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/honeycomb_io"/>
    <language>en</language>
    <item>
      <title>A Culture of Observability Helps Engineers Hit the Spot (Instance)</title>
      <dc:creator>honeycomb</dc:creator>
      <pubDate>Mon, 11 Jan 2021 17:47:25 +0000</pubDate>
      <link>https://dev.to/honeycombio/a-culture-of-observability-helps-engineers-hit-the-spot-instance-16nf</link>
      <guid>https://dev.to/honeycombio/a-culture-of-observability-helps-engineers-hit-the-spot-instance-16nf</guid>
      <description>&lt;p&gt;At Honeycomb, we’re big fans of AWS Spot Instances. During a &lt;a href="https://www.honeycomb.io/blog/treading-in-haunted-graveyards/"&gt;recent bill reduction exercise&lt;/a&gt;, we found significant savings in running our API service on Spot, and now look to use it wherever we can. Not all services fit the mold for Spot, though. While a lot of us are comfortable running services atop on-demand EC2 instances by now, with hosts that could be terminated or fail at any time, this is relatively rare when compared with using Spot, where sudden swings in the Spot market can result in your instances going away with only two minutes of warning. When we considered running a brand-new, stateful, and time-sensitive service on Spot, it seemed like a non-starter, but the potential upside was too good to not give it a try.&lt;/p&gt;

&lt;p&gt;Our strong culture of observability and the environment it enables means we have the ability to experiment safely. With a bit of engineering thoughtfulness, modern deployment processes, and good instrumentation we were able to make the switch with confidence. Here's how we did it:&lt;/p&gt;

&lt;h2&gt;What we needed to support&lt;/h2&gt;

&lt;p&gt;We're working on a feature that enables you to do trace-aware sampling with just a few clicks in our UI. The backend service (internal name Dalmatian) is responsible for buffering trace events and making sampling decisions on the complete trace. To work correctly, it must:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;process a continuous, unordered stream of events from the API as quickly as possible&lt;/li&gt;
    &lt;li&gt;group and buffer related events long enough to make a decision&lt;/li&gt;
    &lt;li&gt;avoid re-processing the same traces, and make consistent decisions for the same trace ID for spans that arrive late&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To accomplish grouping, we use a deterministic mapping function to route trace IDs to the same Kafka partition, with each partition getting processed by one Dalmatian host. To keep track of traces we’ve already processed, we also need this pesky little thing called state. Specifically:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;the current offset in the event stream,&lt;/li&gt;
    &lt;li&gt;the offset of the oldest seen trace not yet processed,&lt;/li&gt;
    &lt;li&gt;recent history of processed trace IDs and their sampling decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This state is persisted to a combination of Redis and S3, so Dalmatian doesn’t need to store anything locally between restarts. Instead, state is regularly flushed to external storage. That’s easy. Startup is more complex, however, as there are some precise steps that need to happen to resume event processing correctly. There’s also a lot of state fetching to do, which adds time and can fail. In short, once the service is running, it’s better to not rock the boat and just let it run. Introducing additional chaos with hosts that come and go more frequently was something to consider before running this service on Spot.&lt;/p&gt;

&lt;h2&gt;Chaos by default&lt;/h2&gt;

&lt;p&gt;As part of the observability-centric culture we strive for at Honeycomb, we &lt;a href="https://www.honeycomb.io/blog/working-on-hitting-a-release-cadence-ci-cd-observability-can-help-you-get-there/"&gt;embrace CI/CD&lt;/a&gt;. Deploys go out every hour. Among the many benefits of this approach is that our deploy system is regularly exercised, and our services undergo restarts all the time. Building a stateful service with a complex startup and shutdown sequence in this environment means that bugs in those sequences are going to show themselves very quickly. By the time we considered running Dalmatian on Spot (queue the puns), we’d already made our state management stable through restarts, and most of the problems Spot might have introduced were already handled.&lt;/p&gt;

&lt;h2&gt;Hot spare Spots&lt;/h2&gt;

&lt;p&gt;There was one lingering issue with using Spot though: having hosts regularly go away means we need to wait on new hosts to come up. Between ASG response times, Chef, and our deploy process, it averages 10 minutes for a new host to come online. It’s something we hope to improve one day, but we’re not there yet. With one processing host per partition, that means losing a host can result in at least a 10 minute delay in event processing. That’s a bad experience for our customers, and one we’d like to avoid. Fortunately, Spot instances are cheap, and since we’re averaging a 70% savings on instance costs, we can afford to run with extra hosts in standby mode. This is accomplished with a bit of extra terraform code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;min_size&lt;/span&gt;                  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_instance_count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_instance_count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;max_size&lt;/span&gt;                  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_instance_count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_instance_count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a small modification in our process startup code, instances will wait for a partition to become free and immediately pick up that partition’s workload.&lt;/p&gt;

&lt;h2&gt;Spot how we're doing&lt;/h2&gt;

&lt;p&gt;We’ve made a significant change to how we run this service. How do we know if things are working as intended? Well, let’s define &lt;b&gt;working&lt;/b&gt;, aka our &lt;a href="https://www.honeycomb.io/blog/working-toward-service-level-objectives-slos-part-1/"&gt;Service Level Objective&lt;/a&gt;. We think that significant ingestion delays break our core product experience, so for Dalmatian and the new feature, we set an SLO that events should be processed and delivered in under five minutes, 99.9% of the time.&lt;/p&gt;

&lt;p&gt;With the SLO for context, we can restate our question as: Can we move this new functionality onto Spot Fleets and still maintain our Service Level Objective despite the extra host churn?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KHt38xKR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/10/replacing-hosts.png" class="article-body-image-wrapper"&gt;&lt;img class="aligncenter size-full wp-image-5251" src="https://res.cloudinary.com/practicaldev/image/fetch/s--KHt38xKR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/10/replacing-hosts.png" alt="screenshot of heatmap with trailing edges showing added hosts" width="767" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the last month in our production environment, we have observed at least 10 host replacements. This is higher than usual churn than we’ve had in the past, but also what we expected with Spot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ENmsXp5t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/10/slo-compliance.png" class="article-body-image-wrapper"&gt;&lt;img class="aligncenter size-full wp-image-5252" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ENmsXp5t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/10/slo-compliance.png" alt="screenshot showing 100% SLO compliance graph" width="683" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that same month, we stayed above our threshold for SLO compliance (99.9%). From this perspective, it looks like a successful change - one we’re excited about because it saves us a significant amount in operational expenses.&lt;/p&gt;

&lt;h2&gt;New firefighting capabilities must be part of the plan&lt;/h2&gt;

&lt;p&gt;Things are working fine now, but they might break in the future. Inevitably, we will find ourselves running behind in processing events. One set of decisions we had to make for this service was “what kind of instance do we run on, and how large?”. When we’re keeping up with incoming traffic, we can run on smaller instance types. If we ever fall behind, though, we need more compute to catch up as quickly as we can, but that’s expensive to run all the time when we may only need it once a year. Since we have a one processor per partition model, we can’t really scale out dynamically. But we can scale up!&lt;/p&gt;

&lt;p&gt;Due to the previously mentioned 10 min host bootstrapping time, swapping out every host with a larger instance is not ideal. We’ll fall further behind while we wait to bring the instances up, and once we decide to scale down, we’ll fall behind again. What if we stood up an identically sized fleet with larger instances and cut over to that? This Terraform spec defines what we call our “catch-up fleet”. The ASG exists only when &lt;code&gt;dalmatian_catchup_instance_count&lt;/code&gt; is greater than 0. In an emergency, a one-line diff can bring this online.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_autoscaling_group"&lt;/span&gt; &lt;span class="s2"&gt;"dalmatian_catchup_asg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dalmatian_catchup_${var.env}"&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_catchup_instance_count&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="c1"&gt;# ...&lt;/span&gt;

  &lt;span class="nx"&gt;launch_template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;launch_template_specification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;launch_template_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_launch_template&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_lt&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"$Latest"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"override"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dalmatian_catchup_instance_types&lt;/span&gt;
      &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the hosts are ready, we can cut over to them by stopping all processes on the smaller fleet, enabling the “hot spares” logic to kick in on the backup fleet. When we’re caught up, we can repeat the process in reverse: start the processes on the smaller fleet, and stop the larger fleet. Using Honeycomb, of course, we were able to verify that this solution works with a fire drill in one of our smaller environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZACynbFF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/10/cutover.png" class="article-body-image-wrapper"&gt;&lt;img class="aligncenter size-full wp-image-5250" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZACynbFF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2019/10/cutover.png" alt="screenshot of the cutover timeframe, validating low latency" width="726" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Spot the benefits of an observability-centric culture&lt;/h2&gt;

&lt;p&gt;Ok maybe that's enough spot-related puns, but the point here is that our ability to experiment with new architectures and make significant changes to how services operate is predicated on our ability to deploy rapidly and find out how those changes impact the behavior of the system. Robust CI/CD processes, thoughtful and context-rich instrumentation, and attention to what we as software owners will need to do to support this functionality in the future makes it easier and safer to build and ship software our users love and benefit from.&lt;/p&gt;




&lt;p&gt;Ready to take the sting out of shipping new software? Honeycomb helps you spot outliers and find out how your users experience your service. &lt;a href="https://www.honeycomb.io/product-trial/"&gt; &lt;/a&gt;&lt;a href="https://ui.honeycomb.io/signup?&amp;amp;utm_source=Devto&amp;amp;utm_Devto=blog&amp;amp;utm_campaign=referral&amp;amp;utm_keyword=%7Bkeyword%7D&amp;amp;utm_content=a-culture-of-observability-helps-engineers-hit-the-spot-instance"&gt;Get started for free!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>observability</category>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Instrumenting Lambda with Traces: A Complete Example in Python</title>
      <dc:creator>honeycomb</dc:creator>
      <pubDate>Mon, 04 Jan 2021 22:02:15 +0000</pubDate>
      <link>https://dev.to/honeycombio/instrumenting-lambda-with-traces-a-complete-example-in-python-gnk</link>
      <guid>https://dev.to/honeycombio/instrumenting-lambda-with-traces-a-complete-example-in-python-gnk</guid>
      <description>&lt;p&gt;We’re big fans of AWS Lambda at Honeycomb. As you &lt;a href="https://www.honeycomb.io/blog/secondary-storage-to-just-storage/"&gt;may have read&lt;/a&gt;, we recently made some major improvements to our storage engine by leveraging Lambda to process more data in less time. Making a change to a complex system like our storage engine is daunting, but can be made less so with good instrumentation and tracing. For this project, that meant getting instrumentation out of Lambda and into Honeycomb. Lambda has some unique constraints that make this more complex than you might think, so in this post we’ll look at instrumenting an app from scratch.&lt;/p&gt;

&lt;h2&gt;Bootstrapping the app&lt;/h2&gt;

&lt;p&gt;Before we begin, you’ll want to ensure you have the &lt;a href="https://serverless.com/framework/docs/getting-started/"&gt;Serverless&lt;/a&gt; framework installed and a recent Python version (I’m using 3.6). For this example, I picked a Serverless Python TODO API template in the Serverless &lt;a href="https://github.com/serverless/examples"&gt;Examples&lt;/a&gt; repo. I like this particular template for demonstration because it sets up an external dependency (DynamoDB), which gives us something interesting to look at when we’re tracing. To install the demo app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sls &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/serverless/examples/tree/master/aws-python-rest-api-with-dynamodb &lt;span class="nt"&gt;--name&lt;/span&gt; my-sls-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a project directory with some contents like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-sls-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="go"&gt;README.md    package.json    serverless.yml    todos
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s just a bit more to add before we get going. We want to install &lt;code&gt;honeycomb-beeline&lt;/code&gt; in our app, so we’ll need to package the Python requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;install &lt;/span&gt;the serverless-python-requirements module
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; serverless-python-requirements
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;install &lt;/span&gt;beeline-python &lt;span class="k"&gt;in &lt;/span&gt;a venv, &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;the requirements.txt
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;virtualenv venv &lt;span class="nt"&gt;--python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python3
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;honeycomb-beeline
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip freeze &amp;amp;gt&lt;span class="p"&gt;;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now edit &lt;code&gt;serverless.yml&lt;/code&gt; and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# essentially, this injects your python requirements into the package&lt;/span&gt;
&lt;span class="c1"&gt;# before deploying. This runs in docker if you're not deploying from a linux host&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-python-requirements&lt;/span&gt;

&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pythonRequirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dockerizePip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;non-linux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can deploy using &lt;code&gt;sls deploy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Serverless: Packaging service...
[...]
endpoints:
  POST - https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos
  GET - https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos
  GET - https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos/{id}
  PUT - https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos/{id}
  DELETE - https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos/{id}
functions:
  create: my-sls-app-dev-create
  list: my-sls-app-dev-list
  get: my-sls-app-dev-get
  update: my-sls-app-dev-update
  delete: my-sls-app-dev-delete
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few curl calls against the new endpoints confirm we’re in good shape!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;db is empty initially
&lt;span class="go"&gt;[]
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text": "write a blog post"}'&lt;/span&gt; https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos
&lt;span class="go"&gt;{"id": "267199de-25c0-11ea-82d7-e6f595c02494", "text": "write a blog post", "checked": false, "createdAt": "1577131779.711644", "updatedAt": "1577131779.711644"}
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos
&lt;span class="go"&gt;[{"checked": false, "createdAt": "1577131779.711644", "text": "write a blog post", "id": "267199de-25c0-11ea-82d7-e6f595c02494", "updatedAt": "1577131779.711644"}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;Implementing tracing&lt;/h2&gt;

&lt;p&gt;The first step is to initialize the beeline. The &lt;code&gt;todos/&lt;strong&gt;init&lt;/strong&gt;.py&lt;/code&gt; file is a great place to drop that init code so that it gets pulled in by all of the Lambda handlers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;beeline&lt;/span&gt;
&lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writekey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'YOURWRITEKEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'todo-app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'my-sls-app'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s look at that curl we ran earlier. That’s hitting the &lt;code&gt;list&lt;/code&gt; function on the API. Let’s open this up and add some instrumentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ...
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;beeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;beeline.middleware.awslambda&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;beeline_wrapper&lt;/span&gt;

&lt;span class="c1"&gt;# The beeline_wrapper decorator wraps the Lambda handler here in a span. By default,
# this also starts a new trace. The span and trace are finished when the function exits
# (but before the response is returned)
&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;beeline_wrapper&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'DYNAMODB_TABLE'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_context_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"table_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# This call to our db dependency is worth knowing about - let's wrap it in a span.
&lt;/span&gt;    &lt;span class="c1"&gt;# That's easy to do with a context manager.
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"db-scan"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# fetch all todos from the database
&lt;/span&gt;        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# .. capture any results we want to include from this function call
&lt;/span&gt;    &lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_context&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="s"&gt;'status_code'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'num_items'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'Items'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# create a response
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'Items'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;decimalencoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DecimalEncoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another &lt;code&gt;sls deploy&lt;/code&gt; gets our instrumentation out there. Once deployed, I can re-run that curl again. Now I have a trace in my dataset!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aQftfGW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/01/example-trace1.png" class="article-body-image-wrapper"&gt;&lt;img class="alignnone size-full wp-image-5765" src="https://res.cloudinary.com/practicaldev/image/fetch/s--aQftfGW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/01/example-trace1.png" alt="screenshot of an individual trace in honeycomb" width="804" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trace shows me a Lambda runtime of 6.7ms, but from the client, it seemed slow, so I check the Lambda execution logs and see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPORT RequestId: def28374-1e35-429b-8667-3ab481b61ad4 Duration: 37.27 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why the discrepancy?&lt;/p&gt;

&lt;h2&gt;The instrumentation performance tax&lt;/h2&gt;

&lt;p&gt;The lifecycle of a Lambda container is tricky. You may already know about cold starts. That is, your code isn’t really running until it is invoked, and when it does run, the container has to start up and this can add latency to your initial invocation(s). Once your function returns a response, it goes into a frozen state, and execution of any running threads is suspended. From there, the container may be reused (without the cold start penalty) by subsequent requests, or terminated. This termination is not done gracefully, and thus the running function isn't given a chance to do housekeeping.&lt;/p&gt;

&lt;p&gt;What does that mean if you’re trying to send telemetry?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Typical client patterns using delayed batching, like those used in our SDKs, are unreliable in Lambda, since the batch transmission thread(s) may never get a chance to run after the function exits.&lt;/li&gt;
  &lt;li&gt;Anything that needs to be sent reliably must be sent before the function returns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why if you crack open the Beeline middleware code for Lambda, you’ll see this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_beeline&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our way of ensuring event batches are sent before the function exits. But it isn't free. The effect of this is that it delays response to the client by the round-trip time from your Lambda to our API. For many use cases, that's an acceptable tradeoff for getting instrumentation out of Lambda. But what if your application (or your user) is latency-sensitive?&lt;/p&gt;

&lt;h2&gt;Cloudwatch logs to the rescue&lt;/h2&gt;

&lt;p&gt;There is one way to synchronously ship data from a Lambda function without significant blocking: logging.&lt;br&gt;
If you want detailed telemetry without a performance hit, this is the currently the only way to go. Each Lambda function writes to its own Cloudwatch Log stream, and AWS makes it relatively easy to subscribe to those streams with other Lambda functions. Here’s where we introduce the &lt;a href="https://github.com/honeycombio/agentless-integrations-for-aws#honeycomb-publisher-for-lambda"&gt;Honeycomb Publisher for Lambda&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Publisher can subscribe to one or more Cloudwatch Log streams, pulling in event data in JSON format and shipping it to the correct dataset. To use it, you need to deploy it and subscribe it to your Lambda function(s) log streams, then configure your app to emit events via logs.&lt;/p&gt;

&lt;h3&gt;Switching to log output&lt;/h3&gt;

&lt;p&gt;The Python Beeline makes it &lt;a href="https://docs.honeycomb.io/getting-data-in/python/beeline/#customizing-event-transmission"&gt;easy&lt;/a&gt; to override the event transmission mechanism. For our purpose here, we’ll use the built-in &lt;code&gt;FileTransmission&lt;/code&gt; with output to sys.stdout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;beeline&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;libhoney&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;libhoney.transmission&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileTransmission&lt;/span&gt;
&lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;writekey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'can be anything'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'todo-app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'my-sls-app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;transmission_impl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FileTransmission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all we need to do to have events flow to Cloudwatch instead of the Honeycomb API. Note that you do not need to set a valid writekey when using the FileTransmission. The Publisher will have responsibility for authenticating API traffic.&lt;/p&gt;

&lt;p&gt;If you execute your app after deploying this, you should see span data in the Cloudwatch Logs for the Lambda function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2019-12-17T16:54:20.355317Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"samplerate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"dataset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-sls-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"service_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"meta.beeline_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.11.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.47&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also note the shorter Lambda run time, now that we aren’t blocking on an API call before exiting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPORT RequestId: 30ff2be3-2c0b-4869-8da3-7af280dfc76c Duration: 9.41 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;Deploying the Publisher&lt;/h3&gt;

&lt;p&gt;The Publisher is another Lambda function, so if you are familiar with integrating third-party Lambda functions in your stack, you should use the tools and methods that work best for you. We provide a helpful, generic &lt;a href="https://github.com/honeycombio/agentless-integrations-for-aws#honeycomb-publisher-for-lambda"&gt;Cloudformation Template&lt;/a&gt; to walk you through installation of it and to document its AWS dependencies. Since we’re using Serverless in this tutorial, though, let’s see if we can make a it a “native” part of our project!&lt;/p&gt;

&lt;p&gt;Serverless allows you to describe ancillary AWS resources to spin up alongside your stack. In our case, we want to bolt on the publisher and its dependencies whenever we deploy our app. In the &lt;code&gt;serverless.yml&lt;/code&gt;, append the following to the &lt;code&gt;resources&lt;/code&gt; block of the sample app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;TodosDynamoDbTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="na"&gt;PublisherLambdaHandler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Lambda::Function'&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;S3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;honeycomb-integrations-${opt:region, self:provider.region}&lt;/span&gt;
          &lt;span class="na"&gt;S3Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agentless-integrations-for-aws/LATEST/ingest-handlers.zip&lt;/span&gt;
        &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lambda function for publishing asynchronous events from Lambda&lt;/span&gt;
        &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;HONEYCOMB_WRITE_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOURHONEYCOMBKEY'&lt;/span&gt;
            &lt;span class="na"&gt;DATASET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-sls-app'&lt;/span&gt;
        &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublisherLambdaHandler-${self:service}-${opt:stage, self:provider.stage}&lt;/span&gt;
        &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publisher&lt;/span&gt;
        &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt;
        &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fn::GetAtt"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LambdaIAMRole&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Arn&lt;/span&gt;
        &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go1.x&lt;/span&gt;
        &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;ExecutePermission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::Lambda::Permission"&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lambda:InvokeFunction'&lt;/span&gt;
        &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fn::GetAtt"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PublisherLambdaHandler&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Arn&lt;/span&gt;
        &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs.amazonaws.com'&lt;/span&gt;
    &lt;span class="na"&gt;LambdaIAMRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role"&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17"&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow"&lt;/span&gt;
              &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda.amazonaws.com"&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole"&lt;/span&gt;
    &lt;span class="na"&gt;LambdaLogPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Policy"&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda-create-log"&lt;/span&gt;
        &lt;span class="na"&gt;Roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LambdaIAMRole&lt;/span&gt;
        &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17"&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:CreateLogGroup&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:CreateLogStream&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs:PutLogEvents&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:logs:*:*:*'&lt;/span&gt;
    &lt;span class="c1"&gt;# add one of me for each function in your app&lt;/span&gt;
    &lt;span class="na"&gt;CloudwatchSubscriptionFilterList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::Logs::SubscriptionFilter"&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DestinationArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fn::GetAtt"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PublisherLambdaHandler&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Arn&lt;/span&gt;
        &lt;span class="na"&gt;LogGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/aws/lambda/${self:service}-${opt:stage, self:provider.stage}-list&lt;/span&gt;
        &lt;span class="na"&gt;FilterPattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
      &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ExecutePermission&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s a lot of boilerplate! The main things you’ll want to take note of is setting a valid value for &lt;code&gt;HONEYCOMB_WRITE_KEY&lt;/code&gt; and add &lt;code&gt;SubscriptionFilter&lt;/code&gt; resources for each function in your stack. Run &lt;code&gt;sls deploy&lt;/code&gt; one more time and you should now see a new function, &lt;code&gt;PublisherLambdaHandler-my-sls-app-dev&lt;/code&gt;, deployed alongside your Lambda functions. The Publisher will be subscribed to your app’s Cloudwatch Log Streams and will forward events to Honeycomb.&lt;/p&gt;

&lt;h2&gt;Leveling up with distributed tracing&lt;/h2&gt;

&lt;p&gt;Lambda functions don’t always run in a vacuum - often they are executed as part of a larger chain of events. You can link your Lambda instrumentation with your overall application instrumentation with distributed traces. To do this, you need to pass trace context from the calling app to your Lambda-backed service. Let’s look at a practical example by building on our existing app. We already have a todo API implemented in Lambda. Let’s assume we’re building a UI on top of that. We have some code that fetches the list of items from the API and then renders them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"todo_list_view"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;todo_list_view&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"requests_get"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;todo_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;render_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"render_list"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTTP request to fetch the list of todo items is done using the &lt;code&gt;requests&lt;/code&gt; lib. This &lt;code&gt;get&lt;/code&gt; call is wrapped in a trace span to measure the request time. We know that &lt;code&gt;list&lt;/code&gt; is instrumented with tracing, so it would be nice if we could link those spans to our trace!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"todo_list_view"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;todo_list_view&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"requests_get"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_beeline&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;tracer_impl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;marshal_trace_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;todo_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'https://mtd8iptz1m.execute-api.us-east-2.amazonaws.com/dev/todos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'X-Honeycomb-Trace'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;render_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;marshal_trace_context&lt;/code&gt; builds a serialized trace context object, which can be passed to other applications via request header or payload. The Lambda middleware automatically looks for a header named &lt;code&gt;X-Honeycomb-Trace&lt;/code&gt; and extracts the context object, adopting the trace ID and caller’s span ID as its parent rather than starting an all-new trace. All we need to do is pass this context object as a request header before we call the &lt;code&gt;list&lt;/code&gt; endpoint. Adding the header is demonstrated manually here, but the beeline provides a patch for the requests lib that will do this for you. Simply import &lt;code&gt;beeline.patch.requests&lt;/code&gt; into your application after importing the &lt;code&gt;requests&lt;/code&gt; lib.&lt;/p&gt;

&lt;p&gt;When we call &lt;code&gt;todo_list_view&lt;/code&gt; in our UI app, we now see one trace with spans from both services:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6h_wrk8r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/01/example-trace2.png" class="article-body-image-wrapper"&gt;&lt;img class="alignnone size-full wp-image-5766" src="https://res.cloudinary.com/practicaldev/image/fetch/s--6h_wrk8r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/01/example-trace2.png" alt="screenshot of an individual trace in the honeycomb ui" width="792" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Go Serverless with confidence&lt;/h2&gt;

&lt;p&gt;Building and running apps with Lambda can be complex and is not without challenges, but with distributed tracing and rich instrumentation, there’s no need to fumble around in the dark.&lt;/p&gt;

&lt;p&gt;Ready to instrument your serverless apps? Get started with &lt;a href="https://ui.honeycomb.io/signup?&amp;amp;utm_source=Devto&amp;amp;utm_Devto=blog&amp;amp;utm_campaign=referral&amp;amp;utm_keyword=%7Bkeyword%7D&amp;amp;utm_content=instrumenting-lambda-with-traces-a-complete-example-in-python"&gt;Honeycomb for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>observability</category>
      <category>tracing</category>
      <category>aws</category>
    </item>
    <item>
      <title>Unpacking Events: All the Better to Observe</title>
      <dc:creator>honeycomb</dc:creator>
      <pubDate>Mon, 16 Nov 2020 17:28:52 +0000</pubDate>
      <link>https://dev.to/honeycombio/unpacking-events-all-the-better-to-observe-1agk</link>
      <guid>https://dev.to/honeycombio/unpacking-events-all-the-better-to-observe-1agk</guid>
      <description>&lt;p&gt;&lt;span&gt;At Honeycomb, we’ve been listening to your feedback. You want easier ways to predict usage and scale your observability spend with your business. What would it look like to meet you where you already are, using similar terms, and give you more control with a simpler experience? We think that means reimagining the customer experience into one that centers around an event-based model.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;But what exactly is an event? What does that mean for your team’s observability journey?&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;span&gt;The Old Way&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;Traditionally, Honeycomb has charged customers based on two axes: ingest and retention. We figured not everyone has the same needs, so why not have you select what’s right for you? But in practice, that translated into placing more administrative burden on you.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Many of you told us that you’d frequently visit the Usage page to fiddle with the slider, responding to spikes in traffic (and therefore ingest) by reducing your retention period in order to keep your bill around the same amount each month. We started to see that become a difficult (but common) tradeoff: if you want to send more data, then it wouldn’t stick around as long. Many of you ended up robbing Peter to pay Paul.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;In addition, we found that it’s not intuitive for many of you to estimate ingest in gigabytes. As a result, many Usage page sliders were adjusted reactively, without much sense of how that would affect Honeycomb cost as more systems were integrated or as service traffic continued to grow.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;span&gt;Stress-Free Observability&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;Let’s face it, that’s not a great experience. If we could take what we’ve learned from you to make it better, what would we do?&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;You can’t always anticipate traffic spikes. A great experience would not penalize you for those.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;You don’t want to obsess over usage every month or have to explain variations in spend to your accounting team. A great experience would let you set your monthly or yearly spend and forget about it, knowing you have headroom for growth.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;You want to stop worrying about retention and capacity planning. We’ve worked with many teams who limit their retention to 24 hours, or even less! A great experience would give you an extended time frame with your data. In the beginning, you would have space to get comfortable with your new instrumentation. Over time, it would allow you to reflect back on the last two months to support your incident review and capacity planning needs.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;A great experience would encourage you to send us wide events with many context fields, since that’s the richness you need for observability. You shouldn’t have to worry about how much data each of those events sends.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;What we’ve found is that as you progress on your observability journey, your instrumentation will reach an equilibrium. Once you figure out your right level of instrumentation, your usage should be predictable and aligned with your application’s traffic patterns. You want to spend time thinking about improving your application, not optimizing fiddly usage sliders.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;span&gt;You Already Think About Events&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;In thinking about how to meet you where you already are, it makes a lot of sense to land on the &lt;/span&gt;&lt;b&gt;event&lt;/b&gt;&lt;span&gt; as the core unit of measure.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;But not all events are scoped similarly. Let’s define what an “event” would mean to Honeycomb, and how that relates to the events you care about for your service.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Honeycomb defines an event as a single “unit of work” in your application code. But a “unit of work” can have a dozen meanings in a dozen contexts. It can be as small as flipping a bit or as large as a round-trip HTTP request. The simplest definition is that an event is usually either a &lt;/span&gt;&lt;b&gt;trace span&lt;/b&gt;&lt;span&gt;, or a &lt;/span&gt;&lt;b&gt;log event&lt;/b&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Let’s unpack that a bit further.&lt;/span&gt;&lt;/p&gt;

&lt;h3&gt;&lt;span&gt;Log Events&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;span&gt;For logs, enumerating events is pretty straightforward. If you use (or if you’re planning to use) &lt;/span&gt;&lt;a href="https://docs.honeycomb.io/getting-data-in/integrations/honeytail/"&gt;&lt;span&gt;honeytail&lt;/span&gt;&lt;/a&gt;&lt;span&gt; to send structured logs into Honeycomb, you likely already know how many log events you’re sending. For infrastructure teams with less authority over code changes, installing an agent like honeytail, the &lt;/span&gt;&lt;a href="https://docs.honeycomb.io/getting-data-in/integrations/aws/"&gt;&lt;span&gt;AWS integrations&lt;/span&gt;&lt;/a&gt;&lt;span&gt;, or the newly-upgraded &lt;/span&gt;&lt;a href="https://docs.honeycomb.io/getting-data-in/integrations/kubernetes/"&gt;&lt;span&gt;Kubernetes agent&lt;/span&gt;&lt;/a&gt;&lt;span&gt; is the best way to get data into Honeycomb. Each event in these agents corresponds to one event in Honeycomb.&lt;/span&gt;&lt;/p&gt;

&lt;h3&gt;&lt;span&gt;Trace Spans and Events&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;&lt;span&gt;With spans, we need to examine the definition a bit more. To start: we’ve decided that one span equals one event.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;If you want high-res observability into your systems, you’ve probably looked into trace-aware instrumentation like our &lt;a href="https://docs.honeycomb.io/getting-data-in/beelines/"&gt;Beelines&lt;/a&gt;. &lt;a href="https://www.honeycomb.io/search/tracing"&gt;We’ve already written a lot about the benefits of tracing.&lt;/a&gt; For this post, let’s focus on what &lt;/span&gt;&lt;b&gt;1 span == 1 event &lt;/b&gt;&lt;span&gt;would mean for adding trace-aware instrumentation to your service.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;As a service owner, you already have your own concept of events in your world: HTTP or API requests, background tasks, queue events, etc. If your app is in production, you probably know your traffic patterns, i.e.:&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;span&gt;How many of your service’s events you experience at any particular time scale—per second, per hour, per day, per month&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;span&gt;The seasonality of those events at different times of the day, week, month, year&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span&gt;When you’ve instrumented for tracing, one of your service’s events generates a single Honeycomb trace. A trace is made up of one or more spans. Remember that in this world, one span is one event.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;span&gt;Estimating Honeycomb Events&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;From here you can roughly predict your Honeycomb usage as a function of your traffic. For example, you could estimate your monthly usage like so:&lt;/span&gt;&lt;/p&gt;

&lt;pre&gt;&lt;span&gt;  (Number of your service’s events per month)
&lt;/span&gt;&lt;span&gt;× (Number of spans in each service event)
_____________________________________________
=  Honeycomb usage per month&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;&lt;span&gt;So how do you figure out the number of spans for each of your service’s events?&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;A useful guideline: one span gets generated from each method call, down to a certain level of granularity. By “granularity,” we mean how deep you go down the call stack. Sometimes you care about the context in methods being called by other methods, and sometimes you don’t. &lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;For example, you probably care more about what kinds of database queries your controller is making, and less about what arguments went into &lt;code&gt;Math.sum()&lt;/code&gt;. (Don’t let me tell you what’s important, though! You know your code better than I do.)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/05/sql.png"&gt;&lt;img class="aligncenter size-full wp-image-6589" src="https://res.cloudinary.com/practicaldev/image/fetch/s--okcsUzX4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/05/sql.png" alt="" width="1600" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Still, the number of spans generated from one of your service’s events depends on the kind of event that is. In this example, an HTTP request using the &lt;a href="https://docs.honeycomb.io/getting-data-in/ruby/beeline/"&gt;ruby-beeline&lt;/a&gt; Rails integration, generated 18 spans. If you’re calling out to another service like Redis or S3, that will generate more spans.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/05/collapsed.png"&gt;&lt;img class="aligncenter size-full wp-image-6588" src="https://res.cloudinary.com/practicaldev/image/fetch/s--hfmZ3kDn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/05/collapsed.png" alt="" width="1600" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the same HTTP request, fully expanded:&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/05/expanded.png"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/05/expanded.png"&gt;&lt;img class="aligncenter size-full wp-image-6586" src="https://res.cloudinary.com/practicaldev/image/fetch/s--s9K0jlVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/05/expanded.png" alt="" width="1600" height="862"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;&lt;b&gt;What to expect&lt;/b&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;There's no magic number for the “proper” level of granularity in tracing. As you ramp up your use of Honeycomb, you'll make discoveries that'll guide how to further instrument your code. New Honeycomb users often discover long-hidden bugs and inefficiencies when they first instrument for tracing. Aim for higher granularity early on with the goal of learning, finding these inefficiencies, and nipping them in the bud.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;So when you see a high-granularity trace with many spans, ask yourself, “Is this trace valuable?” It’s entirely in the eye of the beholder!&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/05/mysql_trace.png"&gt;&lt;img class="aligncenter size-full wp-image-6587" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZGsXlEzV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/05/mysql_trace.png" alt="" width="1276" height="960"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;After this initial period of discovery, you'll gain familiarity with what a normal trace looks like for various parts of your service. Going forward, you'll be much more interested in the abnormal and better able to tune your instrumentation.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Let’s look back at our estimation formula from up above to figure out exactly what usage to expect:&lt;/span&gt;&lt;/p&gt;

&lt;pre&gt;&lt;span&gt;  (Number of your service’s events per month) &lt;/span&gt;
&lt;span&gt;× (Number of spans in each service event) &lt;/span&gt;
&lt;span&gt;_____________________________________________ 
=  Honeycomb usage per month&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;&lt;span&gt;Plugging in some numbers, let’s say your service gets around 1 million requests per day, or up to 30 million requests per month. If each request sends ~20 spans, then you’re looking at 600 million Honeycomb events per month. If each request sends ~50 spans, you’ll be sending 1.5 billion Honeycomb events per month.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Rather than worrying about storage size and retention periods, in this event-based world you’d be able to quickly ascertain what your usage needs are. In future posts, we’ll cover more about what that means going forward and how to even further optimize your usage with techniques like dynamic sampling.&lt;/span&gt;&lt;/p&gt;




&lt;p&gt;Have questions on how to get started? Want help estimating your event volume? Reach out to our team at &lt;a href="mailto:info@honeycomb.io"&gt;&lt;/a&gt;&lt;a href="mailto:info@honeycomb.io"&gt;info@honeycomb.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not using Honeycomb? &lt;a href="https://ui.honeycomb.io/signup?&amp;amp;utm_source=Devto&amp;amp;utm_Devto=blog&amp;amp;utm_campaign=referral&amp;amp;utm_keyword=%7Bkeyword%7D&amp;amp;utm_content=lets-talk-events"&gt;Get started today, for free&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>tracing</category>
      <category>observability</category>
      <category>span</category>
      <category>logging</category>
    </item>
    <item>
      <title>Using Honeycomb to Investigate a Redis Connection Leak</title>
      <dc:creator>honeycomb</dc:creator>
      <pubDate>Thu, 05 Nov 2020 18:06:36 +0000</pubDate>
      <link>https://dev.to/honeycombio/using-honeycomb-to-investigate-a-redis-connection-leak-1bj3</link>
      <guid>https://dev.to/honeycombio/using-honeycomb-to-investigate-a-redis-connection-leak-1bj3</guid>
      <description>&lt;p&gt;This is a guest post by &lt;a href="https://github.com/ajvondrak" rel="noopener noreferrer"&gt;Alex Vondrak&lt;/a&gt;, Senior Platform Engineer at &lt;a href="https://www.truex.com/" rel="noopener noreferrer"&gt;true[X]&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__308469"&gt;
    &lt;a href="/ajvondrak" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F308469%2F9ad5a268-8d44-46f6-8bdd-509c5d24698d.jpeg" alt="ajvondrak image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ajvondrak"&gt;Alex Vondrak&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ajvondrak"&gt;Backend engineer, programming language theory enthusiast, armchair algebraist.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;





&lt;p&gt;This is the story of how I used Honeycomb to troubleshoot &lt;a href="https://github.com/redis/redis-rb/issues/924" rel="noopener noreferrer"&gt;redis/redis-rb#924&lt;/a&gt; and discovered a surprising workaround.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Emergent failures aren't fun. I've long since learned to dread those subtle issues that occur at the intersection of old code bases, eclectic dependencies, unreliable networking, third party libraries &amp;amp; services, the whims of production traffic, and so on.&lt;/p&gt;

&lt;p&gt;That's why, when &lt;a href="https://redislabs.com/" rel="noopener noreferrer"&gt;RedisLabs&lt;/a&gt; emailed us early in December 2019 about our clients spiking up to ~70,000 connections on a new Redis database, I really didn't want to deal with it. Our application was deployed across ~10 machines with 8 processes apiece - so you'd think &lt;em&gt;maybe&lt;/em&gt; around 100 connections tops, if everything was going smoothly. Yet there the stale connections were.&lt;/p&gt;

&lt;p&gt;Of course, I already knew where the problem came from. The new database was for a workaround we'd just deployed. It was a bit of over-engineering for a very specific use case, and it relied on blocking operations to force parallel HTTP requests into some serial order. The logic is replicated in &lt;a href="https://gist.github.com/ajvondrak/958962a0600cf847465a059210f266e7" rel="noopener noreferrer"&gt;this GitHub gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The important thing to note is that we relied on &lt;a href="https://redis.io/commands/blpop" rel="noopener noreferrer"&gt;&lt;code&gt;BLPOP&lt;/code&gt;&lt;/a&gt; to populate most of our responses. This Redis command blocks the client for a given number of seconds, waiting for data to appear on a list for it to pop off. If no data arrives in the given timeout, it returns nil.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Elapsed Time (sec)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Client 1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Client 2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Client 3&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;0&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BLPOP list 2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BLPOP list 4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;(waiting)&lt;/td&gt;
&lt;td&gt;(waiting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RPUSH list x y z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;nil&lt;/code&gt; (timed out)&lt;/td&gt;
&lt;td&gt;(waiting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;x&lt;/code&gt; (didn’t time out)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We were under stringent timeouts ourselves, so we'd just wait 1 second for this data. Our customers would timeout long before Redis did anyway.&lt;/p&gt;

&lt;p&gt;To add to the stress, this legacy code base was using relatively old/unmaintained software: &lt;a href="https://github.com/postrank-labs/goliath" rel="noopener noreferrer"&gt;Goliath&lt;/a&gt;, &lt;a href="https://github.com/igrigorik/em-synchrony" rel="noopener noreferrer"&gt;em-synchrony&lt;/a&gt;, the &lt;a href="https://github.com/redis/redis-rb/issues/915" rel="noopener noreferrer"&gt;recently-deprecated&lt;/a&gt; Redis synchrony driver, and other such things built atop the often-confusing &lt;a href="https://github.com/eventmachine/eventmachine" rel="noopener noreferrer"&gt;EventMachine&lt;/a&gt;. Suffice to say, we've had our issues in the past.&lt;/p&gt;

&lt;p&gt;RedisLabs instituted a connection cap to stop paging their ops people. With the intervening holidays (and just slow customer ramp-up on the new feature), it wouldn't be until February 2020 that we realized no requests were succeeding because RedisLabs constantly sat at max capacity - no one was able to connect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching in the Dark
&lt;/h2&gt;

&lt;p&gt;What could I do? The Ruby code was as straightforward as it could be. I used the &lt;a href="https://github.com/redis/redis-rb" rel="noopener noreferrer"&gt;redis gem&lt;/a&gt; to initialize a single &lt;code&gt;Redis&lt;/code&gt; instance shared across each process. Nothing but a simple call to the &lt;code&gt;Redis#blpop&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blpop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Was the problem in my application? Redis? EventMachine? I spent weeks trying to read each library's code line-by-line, wishing I could just squint hard enough to understand where it could go wrong. I went through several Hail Mary attempts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I tried sending a &lt;a href="https://redis.io/commands/quit" rel="noopener noreferrer"&gt;&lt;code&gt;QUIT&lt;/code&gt;&lt;/a&gt; command after every request. Nothing.&lt;/li&gt;
&lt;li&gt;I investigated setting a &lt;a href="https://redis.io/topics/clients#client-timeouts" rel="noopener noreferrer"&gt;client timeout&lt;/a&gt;, but that's &lt;a href="https://support.redislabs.com/hc/en-us/articles/203290717-How-do-I-configure-the-timeout-for-my-Redis-connections-" rel="noopener noreferrer"&gt;not configurable on RedisLabs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I tried to monkey-patch underlying EventMachine connections to set &lt;a href="https://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection#comm_inactivity_timeout-instance_method" rel="noopener noreferrer"&gt;inactivity timeouts&lt;/a&gt;. No dice.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only remaining suggestion was to write a script that periodically fed the &lt;a href="https://redis.io/commands/client-list" rel="noopener noreferrer"&gt;&lt;code&gt;CLIENT LIST&lt;/code&gt;&lt;/a&gt; into &lt;a href="https://redis.io/commands/client-kill" rel="noopener noreferrer"&gt;&lt;code&gt;CLIENT KILL&lt;/code&gt;&lt;/a&gt;. That was my reality check: &lt;em&gt;surely&lt;/em&gt; that couldn't be an appropriate fix. What was I missing?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Honeycomb
&lt;/h2&gt;

&lt;p&gt;At the time, we had signed up for Honeycomb, but hadn't really instrumented any of our old systems. I whinged about how much I didn't want to spend time on this decrepit code - exactly because of problems like this. So of course I didn't want to instrument it! But I was getting nowhere with this bug. I didn't know what triggered it, short of production traffic. What's more, it was likely a bug in some library, not my application.&lt;/p&gt;

&lt;p&gt;But hey, this is Ruby: I could just monkey-patch the libraries. It wouldn't be my first time. I even wrote the &lt;a href="https://github.com/honeycombio/beeline-ruby/pull/42" rel="noopener noreferrer"&gt;Redis integration for the Ruby beeline&lt;/a&gt; (full disclosure), and that works by monkey-patching the redis gem.&lt;/p&gt;

&lt;p&gt;So I spun up a canary instance pointing to some frenzied branch of my code where I iteratively added stuff like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="no"&gt;Honeycomb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Redis::Client#connect'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disconnect&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="no"&gt;Honeycomb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Redis::Client#disconnect'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reconnect&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="no"&gt;Honeycomb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Redis::Client#reconnect'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was ugly, but it did the trick. I could finally see the code in action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/07/redis-connection-leak.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.honeycomb.io%2Fwp-content%2Fuploads%2F2020%2F07%2Fredis-connection-leak.png" alt="trace view showing Redis reconnection attempts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It still took some flipping back &amp;amp; forth between the trace and the Redis code, but I finally noticed a pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;BLPOP&lt;/code&gt; command was being wrapped in a client-side read timeout that &lt;em&gt;added&lt;/em&gt; 5 seconds on top of the 1 second I was trying to block for. I didn't expect or even need that.&lt;/li&gt;
&lt;li&gt;By using that read timeout code, connection errors would also be retried endlessly.&lt;/li&gt;
&lt;li&gt;Due to the read timeout, each connection error took the full 6 seconds to surface.&lt;/li&gt;
&lt;li&gt;Each retry would disconnect &amp;amp; reconnect the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One reason the connection errors were likely happening was because of the RedisLabs connection cap. They may still have been happening for mysterious reasons, as in &lt;a href="https://github.com/redis/redis-rb/issues/598" rel="noopener noreferrer"&gt;redis/redis-rb#598&lt;/a&gt;. Regardless, I could observe this endless retry loop triggering a disconnect &amp;amp; reconnect - several of them per request. So I thought: if those connections aren't actually being closed, that could be the source of my idle connections.&lt;/p&gt;

&lt;p&gt;After weeks of endless squinting, guessing, and checking, within a &lt;b&gt;day &lt;/b&gt;of using Honeycomb&lt;b&gt;,&lt;/b&gt; I had an idea actually backed up by data. Carefully inspecting the redis gem's code, I circumvented their read timeout &amp;amp; retry logic with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-redis.blpop(key, timeout: 1)
&lt;/span&gt;&lt;span class="gi"&gt;+redis.call([:blpop, key, 1])
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it was deployed to the whole cluster, our connection count plummeted, and the feature finally worked as intended.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Think about what you're actually trying to do. I could've written a &lt;code&gt;CLIENT KILL&lt;/code&gt; cron job, but that's trying to &lt;em&gt;reap&lt;/em&gt; idle connections, not stop them from being generated in the first place. When you don't have enough information about the problem you're trying to solve, the answer probably isn't to fix some other symptom.&lt;/li&gt;
&lt;li&gt;When the only thing you can do to reproduce an error is to launch production traffic at it, live tracing is the only way to see exactly the details you need.&lt;/li&gt;
&lt;li&gt;The code bases you hate the most are usually the ones that need the most instrumentation. But a little instrumentation still goes a long way.&lt;/li&gt;
&lt;li&gt;Know when to stop. I don't know why this bug happened just the way it did. But I figured out enough to fix the problem for our customers. It's a reasonably precise workaround, and further investigation can go from there.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Download the &lt;a href="https://www.honeycomb.io/resources/guide-observability-for-developers/" rel="noopener noreferrer"&gt;Observability for Developers white paper&lt;/a&gt; to learn more about how observability can help you debug emergent failure modes in production.&lt;/p&gt;

&lt;p&gt;Experience what Honeycomb can do for your business. Check out &lt;a href="https://www.honeycomb.io/get-a-demo" rel="noopener noreferrer"&gt;our short demo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>redis</category>
      <category>debugging</category>
      <category>observability</category>
      <category>honeycomb</category>
    </item>
    <item>
      <title>Logs and Traces: Two Houses Unalike in Dignity</title>
      <dc:creator>honeycomb</dc:creator>
      <pubDate>Fri, 23 Oct 2020 21:21:50 +0000</pubDate>
      <link>https://dev.to/honeycombio/logs-and-traces-two-houses-unalike-in-dignity-3d84</link>
      <guid>https://dev.to/honeycombio/logs-and-traces-two-houses-unalike-in-dignity-3d84</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;a href="https://www.imohealth.com/"&gt;Intelligent Medical Objects&lt;/a&gt; (IMO) and its clinical interface terminology form the foundation healthcare enterprises need, including effective management of Electronic Health Record (EMR) problem lists and accurate documentation. Over 4,500 hospitals and 500,000 physicians use IMO products on a daily basis.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;With Honeycomb, the engineering team at IMO was able to find hidden architectural issues that were previously obscured in their logs. By introducing tracing into their .NET application stack in AWS, they were able to generate new insights that unlocked reliability and efficiency gains.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Michael Ericksen, Sr. Staff Software Engineer at IMO, contributed this guest blog detailing the journey.&lt;/span&gt;&lt;/p&gt;




&lt;p&gt;&lt;span&gt;At IMO, our 2019 engineering roadmap included moving application hosting from multiple data centers into AWS. The primary hosting pattern to migrate was a &lt;/span&gt;&lt;span&gt;.NET&lt;/span&gt;&lt;span&gt; application running on a Windows instance behind a load balancer. In AWS, we use SSM documents written in PowerShell to define the steps to install agents, configure IIS, and perform other machine configuration prior to installing application code. We compose these discrete documents into orchestration documents (more than 25 in total) that execute upon instance launch based on the tags associated with the instance.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;b&gt;Uncovering issues with trace instrumentation&lt;/b&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;Our production applications have predictable load distribution during core business hours. We don’t see traffic spikes that demand instantaneous scaling. Development teams use scheduled Auto Scaling Group actions in our pre-production accounts to toggle capacity after business hours, generating significantly more activity. After a year of reliable operation under lower load parameters, we started hearing reports from internal users that the provisioning service in these pre-prod accounts was intermittently failing.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;However, we didn’t have an easy toolset and vocabulary for describing our service reliability. We had logs! Unstructured ones. Lots of them.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/default-log-output-high-res-e1601333196170.png"&gt;&lt;img class="size-full wp-image-7774" src="https://res.cloudinary.com/practicaldev/image/fetch/s--hJkAUKEN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/default-log-output-high-res-e1601333196170.png" alt="Default log output generated during SSM provisioning"&gt;&lt;/a&gt; Default log output generated during SSM provisioning&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;br&gt;
&lt;span&gt;Debugging issues for end-users meant searching through the copious logs generated by our processes. A dashboard aggregated the number of errors that appeared in the log files. That visibility helped us stabilize the service, but it didn’t provide critical insights to improve service operation for users.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;I had used the &lt;/span&gt;&lt;a href="https://github.com/honeycombio/agentless-integrations-for-aws/blob/main/terraform/cloudwatch-logs-json.tf"&gt;&lt;span&gt;Honeycomb agentless CloudWatch integration&lt;/span&gt;&lt;/a&gt;&lt;span&gt; to ingest structured logs from Lambda functions. I was optimistic I could adopt a similar pattern to provide visibility into our provisioning process. The germ of the idea was simple:&lt;/span&gt;&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;
&lt;span&gt;Write structured logs to &lt;/span&gt;&lt;span&gt;stdout&lt;/span&gt;
&lt;/li&gt;
    &lt;li&gt;&lt;span&gt;Ingest them via CloudWatch&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;span&gt;Forward them to Honeycomb using the agentless CloudWatch integration&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;span&gt;One key technical difference between a Lambda function and SSM documents is that functions hold context while dispatching the next operation. For SSM documents, we pushed in-process spans to a stack saved to the local file system. As spans completed, we popped them from the stack and wrote them as structured JSON logs to CloudWatch. Since our SSM documents execute sequentially, there are minimal concerns about concurrency.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;For more technical implementation details, the source code for the initial version is on &lt;/span&gt;&lt;a href="https://github.com/ericksoen/aws-systems-manager-observability-demo/blob/master/terraform/ec2_instance/functions.ps1"&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;/a&gt;&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/simple-tracing-cloudwatch-stdout-high-res-v2.png"&gt;&lt;img class="wp-image-7772 size-large" src="https://res.cloudinary.com/practicaldev/image/fetch/s--xUARmQDk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/simple-tracing-cloudwatch-stdout-high-res-v2-1024x154.png" alt="CloudWatch agent latency"&gt;&lt;/a&gt; &lt;em&gt;CloudWatch agent latency&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;The first time I saw the trace from this implementation I was certain there was an instrumentation bug: &lt;/span&gt;&lt;i&gt;&lt;span&gt;Why would an SSM document composed of two steps that each write a single line to &lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span&gt;stdout&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span&gt; take more than 25 seconds to complete?!?&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;The provisioning execution time comprises a fractional percentage of the total execution time. That impact is magnified by the number of SSM documents (typically 15-25 documents) that execute during our primary provisioning processes. With trace instrumentation, that behavior was immediately visible.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/default-log-output-highlighted-high-res.png"&gt;&lt;img class="size-full wp-image-7771" src="https://res.cloudinary.com/practicaldev/image/fetch/s--cE_dkGx6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/default-log-output-highlighted-high-res.png" alt="Default log output with highlighting"&gt;&lt;/a&gt; &lt;em&gt;Default log output with highlighting&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;br&gt;
&lt;b&gt;We’d overlooked past performance issues numerous times while debugging other problems because they were obscured by the log structure.&lt;/b&gt;&lt;span&gt; Similarly, the log structure precluded the type of open-ended curiosity we associate with observability:&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;span&gt;What provisioning steps execute the most frequently?&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;span&gt;What provisioning steps fail most frequently?&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;span&gt;Does introducing a new agent version preserve the stability of the system or does it de-stabilized it?&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;b&gt;Debugging idle time in our provisioning service&lt;/b&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;As we debugged potential causes of the issue (shoutout to Narendra from AWS technical support), one detail became clear: &lt;/span&gt;&lt;b&gt;we had two performance issues with a similar signature instead of the one we imagined&lt;/b&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;The first issue presented when writing logs to &lt;/span&gt;&lt;span&gt;stdout&lt;/span&gt;&lt;span&gt; and sending them directly to CloudWatch. This was the eyebrow-raising performance issue detailed above.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;However, our production provisioning service uses a different strategy to ingest logs into CloudWatch. For this second strategy, there was no detectable performance penalty. Yet all of our telemetry continued to show 10-20 second gaps of time between steps. We estimated three to five minutes of idle time during end-to-end execution. Curious.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/tracing-cloudwatch-agent-high-res-v2.png"&gt;&lt;img class="size-full wp-image-7773" src="https://res.cloudinary.com/practicaldev/image/fetch/s--cBWqv4WR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/tracing-cloudwatch-agent-high-res-v2.png" alt="IMO end-to-end provisioning"&gt;&lt;/a&gt; &lt;em&gt;IMO end-to-end provisioning&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;br&gt;
&lt;span&gt;We slowly unveiled a &lt;/span&gt;&lt;i&gt;&lt;span&gt;subtle&lt;/span&gt;&lt;/i&gt;&lt;span&gt; issue in the PowerShell code that executes during our provisioning process. At various points, we use functions from &lt;/span&gt;&lt;span&gt;AWSPowerShell&lt;/span&gt;&lt;span&gt; modules to retrieve configuration from a remote source, usually SSM Parameter Store. There’s a significant &lt;/span&gt;&lt;a href="https://awsinsider.net/articles/2019/08/14/powershell-revamp.aspx"&gt;&lt;span&gt;performance penalty&lt;/span&gt;&lt;/a&gt;&lt;span&gt;  when first importing &lt;/span&gt;&lt;span&gt;AWSPowerShell&lt;/span&gt;&lt;span&gt; modules into your PowerShell session.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;We also learned our strategy for  module imports &lt;/span&gt;&lt;i&gt;&lt;span&gt;“… are always applied globally, and must be met, before the script can execute.”&lt;/span&gt;&lt;/i&gt;&lt;span&gt; The performance penalty for importing &lt;/span&gt;&lt;span&gt;AWSPowerShell&lt;/span&gt;&lt;span&gt; modules occurs outside of &lt;/span&gt;&lt;i&gt;&lt;span&gt;any&lt;/span&gt;&lt;/i&gt;&lt;span&gt; application telemetry that attempts to measure it.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;If you want to experiment with this behavior, we’ve included the steps to reproduce in our &lt;/span&gt;&lt;a href="https://github.com/ericksoen/aws-systems-manager-observability-demo#performance-delay-due-to-awspowershell-modules"&gt;&lt;span&gt;GitHub project&lt;/span&gt;&lt;/a&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;b&gt;Watching code go live to fix the issue&lt;/b&gt;&lt;/h2&gt;

&lt;p&gt;&lt;span&gt;After identifying our implementation of &lt;/span&gt;&lt;span&gt;AWSPowerShell&lt;/span&gt;&lt;span&gt; as the root of our performance issues, we evaluated an array of resolution options. Caching modules across PowerShell sessions wasn’t technically feasible. Restructuring provisioning documents to execute within a single PowerShell session was a significant time commitment. Rewriting our provisioning service to use &lt;/span&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-ec2-image-builder/"&gt;&lt;span&gt;AWS Image Builder&lt;/span&gt;&lt;/a&gt;&lt;span&gt; was the long-term direction we agreed was most appropriate. However, rewrites never happen overnight and change frequently introduces new failures.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;In the interim, we added a new step to install the AWS CLI and modified function calls to &lt;/span&gt;&lt;i&gt;&lt;span&gt;prefer&lt;/span&gt;&lt;/i&gt;&lt;span&gt; using the AWS CLI,before falling back to using &lt;/span&gt;&lt;span&gt;AWSPowerShell&lt;/span&gt;&lt;span&gt; modules if it wasn’t available.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Initially, we deployed the change to our lowest application environment and compared the performance delta.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/romeo-and-juliet.gif"&gt;&lt;img class="aligncenter wp-image-7776 size-full" src="https://res.cloudinary.com/practicaldev/image/fetch/s--H7gAORli--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/romeo-and-juliet-e1601412814521.gif" alt="A young Leonardo DiCaprio in Romeo and Juliet says “But, soft! What light through yonder window breaks?”" width="500" height="200"&gt;&lt;/a&gt; &lt;em&gt;A young Leonardo DiCaprio in Romeo and Juliet says “But, soft! What light through yonder window breaks?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;br&gt;
&lt;span&gt;One of our key performance metrics is time from instance launch until application code begins installation. Our logs made this difficult to calculate for a single instance and impossible to measure at scale. Our trace data, however, made this simple: &lt;/span&gt;&lt;i&gt;&lt;span&gt;measure what matters to your users&lt;/span&gt;&lt;/i&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/performance-improvement-high-res.png"&gt;&lt;img class="size-full wp-image-7775" src="https://res.cloudinary.com/practicaldev/image/fetch/s--QpAW7IFN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/performance-improvement-high-res.png" alt="Graph showing performance improvement between account with CLI change and account without"&gt;&lt;/a&gt; &lt;em&gt;Graph showing performance improvement between account with CLI change and account without&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;br&gt;
&lt;span&gt;The graphs show a significant performance increase when using the AWS CLI. At the 50th percentile, we improved performance by more than 2 minutes and improved performance by more than 4 minutes at the 99th percentile. In fact, the 99th percentile performance metrics when using the AWS CLI was almost as fast as the 50th percentile times when it was not. ¡Que bueno!&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/h2&gt;

&lt;p&gt;&lt;b&gt;Building observability into the foundation of systems can dramatically reveal potential architectural issues long before it reaches customers.&lt;/b&gt;&lt;span&gt; Even experienced peer reviewers frequently cannot predict the way multiple complex systems converge and intersect to magnify performance issues: &lt;/span&gt;&lt;b&gt;we were paying performance penalties once per document, not once per end-to-end execution&lt;/b&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Although this issue could have been discoverable in our logs, it remained opaque to us for more than a year until our tracing revealed it. The trace data we generate also provides new insights into how to operate our system more reliably and efficiently.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.honeycomb.io/wp-content/uploads/2020/09/aggregate-performance-high-res.png"&gt;&lt;img class="size-full wp-image-7770" src="https://res.cloudinary.com/practicaldev/image/fetch/s--a0icf5DC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.honeycomb.io/wp-content/uploads/2020/09/aggregate-performance-high-res.png" alt="Graph showing execution count and duration percentiles for provisioning steps."&gt;&lt;/a&gt; &lt;em&gt;Graph showing execution count and duration percentiles for provisioning steps.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt; &lt;/span&gt;&lt;br&gt;
&lt;span&gt;As seen in the above graph, there’s a significant decline in execution time after our four longest run steps. Moreover, one of those longest running steps executes far less frequently than its peers.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;If we were to only solve the time-gap fix discussed above and then optimize the three most frequently executed, longest running steps, we could improve performance for one of our key user metrics by more than 9 minutes, or 62% of the total execution.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;As Cindy Sridharan notes in her report on &lt;/span&gt;&lt;a href="https://www.oreilly.com/library/view/distributed-systems-observability/9781492033431/"&gt;&lt;span&gt;Distributed System Observability&lt;/span&gt;&lt;/a&gt;&lt;span&gt;:&lt;/span&gt;&lt;/p&gt;

&lt;blockquote&gt;&lt;span&gt;The goal … is not to collect logs, metrics, or traces. It is to build a culture of engineering based on facts and feedback, and then spread that culture within the broader organization&lt;/span&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;span&gt;With remarkable economy of language, another familiar CTO expressed her sentiments even more succinctly: &lt;/span&gt;&lt;a href="https://twitter.com/krisnova/status/1002508776447102976?s=20"&gt;&lt;span&gt;“I’m so done with logs.”&lt;/span&gt;&lt;/a&gt;&lt;span&gt; We’re so done with logs too, Charity.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Join the swarm. Get started with &lt;a href="https://ui.honeycomb.io/signup?&amp;amp;utm_source=Devto&amp;amp;utm_Devto=blog&amp;amp;utm_campaign=referral&amp;amp;utm_keyword=%7Bkeyword%7D&amp;amp;utm_content=logs-and-traces-two-houses-unalike-in-dignity/"&gt;Honeycomb for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tracing</category>
      <category>logging</category>
      <category>aws</category>
      <category>observability</category>
    </item>
  </channel>
</rss>
