<?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>Forem: Paul Singman</title>
    <description>The latest articles on Forem by Paul Singman (@peacing).</description>
    <link>https://forem.com/peacing</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%2F451418%2F2b4d511f-6396-49d6-a616-cac6c03f8876.png</url>
      <title>Forem: Paul Singman</title>
      <link>https://forem.com/peacing</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/peacing"/>
    <language>en</language>
    <item>
      <title>Your AWS Lambda Function Failed, What Now?</title>
      <dc:creator>Paul Singman</dc:creator>
      <pubDate>Tue, 10 Nov 2020 23:25:27 +0000</pubDate>
      <link>https://forem.com/aws-builders/your-aws-lambda-function-failed-what-now-4ik</link>
      <guid>https://forem.com/aws-builders/your-aws-lambda-function-failed-what-now-4ik</guid>
      <description>&lt;p&gt;On the analytics team at Equinox Media, we invoke thousands of Lambda functions daily to perform a variety of data processing tasks. Examples range from the mundane shuffling of files around on S3, to the more stimulating generation of real-time fitness content recommendations on the Variis app.&lt;/p&gt;

&lt;p&gt;Because of our reliance on Lambda, it’s critical to diagnose issues as quickly as possible.&lt;/p&gt;

&lt;p&gt;Here’s a diagram of the process we’ve set up to do so:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fu1getupg3biao9ls26gl.png" class="article-body-image-wrapper"&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%2Fi%2Fu1getupg3biao9ls26gl.png" alt="Serverless error handling architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are also a user of Lambda, what does your error alerting look like? If you find yourself struggling to figure out why a failure occurred, or worse — unaware one happened at all — we hope sharing our solution will help you become a more effective serverless practitioner!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #1: Create An Error Metric-Based CloudWatch Alarm
&lt;/h2&gt;

&lt;p&gt;After every single run of a Lambda function, AWS sends a few metrics to the CloudWatch service by default. Per &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Invocation metrics are binary indicators of the outcome of &amp;gt; an invocation. For example, if the function returns an error, Lambda sends the Errors metric with a value of 1. To get a count of the number of function errors that occurred each minute, view the Sum of the Errors metric with a period of one minute.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To make us aware of any failures, we create a CloudWatch Alarm based on the Errors metric for a specific Lambda resource. The exact threshold of the alarm depends on how frequently a job runs and its criticality, but most commonly this value is set to trigger upon three* failures in a five minute period.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;One for the original failure, plus two automatic retries.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For some, generic alerting of this sort is sufficient, and notifications are simply directed to a work email or perhaps a PagerDuty Service tied to an on-call schedule.&lt;/p&gt;

&lt;p&gt;However, we know in this scenario valuable information about the failed invocation is being ignored. To be most efficient, we strive to automate more of the debugging process.&lt;/p&gt;

&lt;p&gt;Our journey, eager Lambda user, is only beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #2: With A Little Help From An SNS Topic + Lambda Friends
&lt;/h2&gt;

&lt;p&gt;Instead of sending straight to an alerting service, we send alarm notifications to a centralized SNS topic that handles failure events for all Lambda functions across our cloud data infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fblrmito62f8ehk2gn4q4.png" class="article-body-image-wrapper"&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%2Fi%2Fblrmito62f8ehk2gn4q4.png" alt="Configuring a CloudWatch Alarm to send to SNS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happens to an Alarm record sent to the topic? It triggers another Lambda function of course!&lt;/p&gt;

&lt;p&gt;We call this special Lambda function the Alerting Lambda and it performs three main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sends a message to Slack with details about the failure.&lt;/li&gt;
&lt;li&gt;Creates an incident in PagerDuty, also populated with helpful details.&lt;/li&gt;
&lt;li&gt;Queries CloudWatch Logs for log messages related to the failure, and if found, sends to Slack.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two steps are relatively straightforward so we’ll quickly cover how they work before diving into the third.&lt;/p&gt;

&lt;p&gt;If you inspect the payload sent from CloudWatch Alarms to SNS, you’ll see it contains data related to the alarm itself like the name, trigger threshold, old and current alarm state, and relevant CloudWatch Metric.&lt;/p&gt;

&lt;p&gt;The Alerting Lambda takes this data and parses it into a super-helpful Slack message (via a webhook) that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2F8mxxnmt48ytgsep4zgsl.png" class="article-body-image-wrapper"&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%2Fi%2F8mxxnmt48ytgsep4zgsl.png" alt="Slack message from #data-alerts channel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, using the &lt;code&gt;pypd&lt;/code&gt; package we create a PagerDuty event with helpful Custom metrics and AWS console link populated:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fi0l5dlshfit4l5iizb5c.png" class="article-body-image-wrapper"&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%2Fi%2Fi0l5dlshfit4l5iizb5c.png" alt="PagerDuty Incident with Alarm data populated as Custom Events"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both of these notifications help us instantly determine if an alert is legitimate or perhaps falls more into the “false alarm” category. When managing 100+ tasks, this provides a quality-of-life improvement for everyone on the team.&lt;/p&gt;

&lt;p&gt;The third step of the Alerting Lambda is recently implemented (inspired by this post on &lt;a href="https://towardsdatascience.com/why-you-should-never-ever-print-in-a-lambda-function-f997d684a705" rel="noopener noreferrer"&gt;effective Lambda logging&lt;/a&gt;) and has proven to be a beloved shortcut for Lambda debugging.&lt;/p&gt;

&lt;p&gt;The output is a message in Slack containing log messages from recent Lambda failures that looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Frex0pvaisy0ewbockjqm.png" class="article-body-image-wrapper"&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%2Fi%2Frex0pvaisy0ewbockjqm.png" alt="CloudWatch logs automatically appear in Slack!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How does this work exactly?&lt;/p&gt;

&lt;p&gt;The first step is to parse out the Lambda function name from the SNS event. This allows us to know which CloudWatch Log Group to query against for recent errors, shown in the code snippet below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And after parsing the query response for a &lt;code&gt;requestId&lt;/code&gt;, we run a second Insights query filtered on that &lt;code&gt;requestId&lt;/code&gt;, re-format the log messages returned in the response, and send the results to Slack.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Place code like this in your Alerting Lamba and before you know it, you’ll be getting helpful log messages sent to Slack too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Though this solution has proven effective for our needs, there is room for improvement. Notably while we query CloudWatch Logs when a Lambda Errors, we don’t handle other Lambda failures (like timeouts or throttling).&lt;/p&gt;

&lt;p&gt;The idea to run an Insights query when a Lambda fails didn’t come to us in a “Eureka!” moment of inspiration… but rather from observing any consistent, predictable actions we perform that could be automated. Maintaining an awareness for these situations will serve any developer well in his or her career.&lt;/p&gt;

&lt;p&gt;Another lesson for some getting started with serverless technologies is that you cannot be afraid of managing many, many cloud resources. Critically, the marginal cost of adding an additional Lambda function or SQS queue to your architecture should be near-zero.&lt;/p&gt;

&lt;p&gt;The idea of spinning up an additional SNS topic and Lambda for error handling was a turn off to some. We hope we’ve shown the benefits of growing past that limiting mindset. If you want to read more on this topic, check out our post on &lt;a href="https://medium.com/whispering-data/tackling-fragmentation-in-serverless-data-pipelines-b4027f506ee5" rel="noopener noreferrer"&gt;painlessly deploying Lambda&lt;/a&gt; functions.&lt;/p&gt;

&lt;p&gt;One final thought, you may be wondering if all other Lambdas are monitored by the Alerting Lambda, what then, monitors the Alerting Lambda function?&lt;/p&gt;

&lt;p&gt;Hmmm.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>python</category>
    </item>
    <item>
      <title>How I Write Meaningful Tests for AWS Lambda Functions</title>
      <dc:creator>Paul Singman</dc:creator>
      <pubDate>Tue, 15 Sep 2020 03:07:13 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-i-write-meaningful-tests-for-aws-lambda-functions-2ifh</link>
      <guid>https://forem.com/aws-builders/how-i-write-meaningful-tests-for-aws-lambda-functions-2ifh</guid>
      <description>&lt;h4&gt;
  
  
  A Harsh Truth
&lt;/h4&gt;

&lt;p&gt;If you are going to write meaningless unit tests that are more likely to mask errors than expose them, you are better off skipping the exercise altogether.&lt;/p&gt;

&lt;p&gt;There, I said it.&lt;/p&gt;

&lt;p&gt;Your time is precious and could be spent on better things than achieving a hollow coverage percentage.&lt;/p&gt;

&lt;p&gt;Effective testing of code has long been a challenging problem in programming, and newer tools like AWS Lambda seem to bring out the worst in developers when it comes to writing tests.&lt;/p&gt;

&lt;p&gt;I think the main reason for this is that it’s more difficult (or at least less intuitive) to mirror the Lambda production environment locally. And as a result, some developers abstain from testing entirely.&lt;/p&gt;

&lt;p&gt;I know because I’ve done it myself, even for projects in production. Instead, testing was done integration-style only after code was already deployed to the cloud.&lt;/p&gt;

&lt;p&gt;This is extremely manual and wastes time in the long run. Another approach I’ve seen results in tests that look something like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This is the unmistakeable sign of an engineering team with a test coverage requirement but a lack of accountability. And no explanation is needed that the above is a no-no.&lt;/p&gt;

&lt;p&gt;So, how do we go about transforming the sad test_lambda_function.py file above into something meaningful?&lt;/p&gt;

&lt;p&gt;Before we can dive right into testing our Lambda code, there are a couple hurdles in the way. We’ll cover each of these individually and determine how to best handle them. Once dealt with, we are then free to test Lambdas to our heart’s content!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I’ll be including small snippets of code throughout the article for clarity. But at the end there will be a full working code example to reference.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hurdle #1: The Lambda Trigger Event
&lt;/h2&gt;

&lt;p&gt;Every Lambda function gets invoked in response to a pre-defined trigger that passes specific &lt;code&gt;event&lt;/code&gt; data into the default &lt;code&gt;lambda_handler()&lt;/code&gt; method. And your first task for effectively testing a Lambda function is to create a realistic input event to test with.&lt;/p&gt;

&lt;p&gt;The format of this event depends on the type of trigger. As of the time of writing there are 16 distinct AWS services that can act as the invocation trigger for Lambda.&lt;/p&gt;

&lt;p&gt;Below is a code snippet with several examples of inputs that I most commonly use:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The full list of sample input events can be found in the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;. Alternatively, you can also print the event variable in your lambda_handler code after deploying and view the payload in CloudWatch Logs:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fjy37myhuigt4lhwe3d2z.jpeg" class="article-body-image-wrapper"&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%2Fi%2Fjy37myhuigt4lhwe3d2z.jpeg" alt="Lambda event triggered from Kinesis in CloudWatch Logs example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have that example, simply hardcode it in your test file as shown above and we’re off to a fantastic start!&lt;/p&gt;

&lt;p&gt;Next up…&lt;/p&gt;

&lt;h2&gt;
  
  
  Hurdle #2: AWS Service Interactions
&lt;/h2&gt;

&lt;p&gt;Almost inevitably, a Lambda function interacts with other AWS services. Maybe you are writing data to a DynamoDB table. Or posting a message to an SNS topic. Or simply sending a metric to CloudWatch. Or a combination of all three!&lt;/p&gt;

&lt;p&gt;When testing it is not a good idea to send data or alter actual AWS resources used in production. To get around this problem, one approach is to set up and later tear down separate test resources.&lt;/p&gt;

&lt;p&gt;A cleaner approach though, is to mock interactions with AWS services. And since this is a common problem, a package has been developed to solve this specific problem. And what’s better is it does so in a super elegant way.&lt;/p&gt;

&lt;p&gt;It’s name is moto (a portmanteau of mock &amp;amp; boto) and its elegance is derived from two main features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It patches and mocks boto clients in tests automatically.&lt;/li&gt;
&lt;li&gt;It maintains the state of pseudo AWS resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What does this look like? All that’s needed is some decorator magic and a little set up!&lt;/p&gt;

&lt;p&gt;Say we read data from S3 in our Lambda. Instead of creating and populating a test bucket in S3, we can use moto to create a fake S3 bucket — one that looks and behaves exactly like a real one — without actually touching AWS.&lt;/p&gt;

&lt;p&gt;And the best part is we can do this using standard boto3 syntax, as seen in the example below when calling the &lt;code&gt;create_bucket&lt;/code&gt; and &lt;code&gt;put_object&lt;/code&gt; methods:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Similarly, if we write data to DynamoDB, we could set up our test by creating a fake Dynamo table first:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;It requires a bit of trust, but if the test passes, you can be confident your code will work in production, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Okay, but not everything is covered by moto…
&lt;/h2&gt;

&lt;p&gt;Yes, it is true that moto doesn’t maintain parity with every AWS API. For example, if your Lambda function interacts with AWS Glue, odds are moto will leave you high and dry since it is only 5% implemented for the Glue service.&lt;/p&gt;

&lt;p&gt;This is where we need to roll up our sleeves and do the dirty work of mocking calls ourselves by &lt;a href="https://stackoverflow.com/questions/5626193/what-is-monkey-patching" rel="noopener noreferrer"&gt;monkeypatching&lt;/a&gt;. This is true whether we’re talking about AWS-related calls or any external service your Lambda may touch, like when posting a message to Slack, for example.&lt;/p&gt;

&lt;p&gt;Admittedly the terminology and concepts around this get dense, so it is best explained via an example. Let’s stick with AWS Glue and say we have a burning desire to list our account’s Glue crawlers with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;session = boto3.session.Session()
glue_client = session.client("glue", region_name='us-east-1')
glue_client.list_crawlers()['CrawlerNames']
# {“CrawlerNames”: [“crawler1”, "crawler2",...]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If we don’t want the success or failure of our illustrious test to depend on the &lt;code&gt;list_crawlers()&lt;/code&gt; response, we can hardcode a return value like so:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;By leveraging the &lt;code&gt;setattr&lt;/code&gt; method of the pytest monkeypatch fixture, we allow glue client code in a &lt;code&gt;lambda_handler&lt;/code&gt; to dynamically at runtime access the hardcoded &lt;code&gt;list_clusters&lt;/code&gt; response from the MockBotoSession class.&lt;/p&gt;

&lt;p&gt;What’s nice about this solution is that is it flexible enough to work for any boto client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tidying Up with Fixtures
&lt;/h3&gt;

&lt;p&gt;We’ve already covered how to deal with event inputs and external dependencies in Lambda tests. Another tip I’d like to share involves the use of pytest &lt;a href="https://docs.pytest.org/en/stable/fixture.html" rel="noopener noreferrer"&gt;fixtures&lt;/a&gt; to maintain an organized testing file.&lt;/p&gt;

&lt;p&gt;The code examples thus far have shown set up code directly in the &lt;code&gt;test_lambda_handler&lt;/code&gt; method itself. A better pattern, however, is to create a separate &lt;code&gt;set_up&lt;/code&gt; function as a pytest fixture that gets passed into any test method that needs to use it.&lt;/p&gt;

&lt;p&gt;For the final code snippet, let’s show an example of this fixture structure using the &lt;code&gt;@pytest.fixture&lt;/code&gt; decorator and combine everything covered:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We’ve come a long way from the empty test file at the beginning of the article, no?&lt;/p&gt;

&lt;p&gt;As a reminder, this code tests a Lambda function that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Triggers off an sqs message event&lt;/li&gt;
&lt;li&gt;Writes the message to a Dynamo Table&lt;/li&gt;
&lt;li&gt;Lists available Glue Crawlers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By employing these strategies though, you should feel confident testing a Lambda triggered from any event type, and one that interacts with any AWS service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;If you’ve been struggling to test your Lambda functions, my hope is this article showed you a few tips to help you do so in a useful way.&lt;/p&gt;

&lt;p&gt;While we spent a lot of time on common issues and how you &lt;em&gt;shouldn’t&lt;/em&gt; test a Lambda, we didn’t get a chance to cover the opposite, yet equally important aspect of this topic--namely what &lt;em&gt;should&lt;/em&gt; you test, and how can you structure your Lambda function’s code to make it easier to do so.&lt;/p&gt;

&lt;p&gt;I look forward to hearing from you and discussing how you test Lambda functions!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you to Vamshi Rapolu for inspiration and feedback on this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>python</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why You Should Never, Ever print() in a Lambda Function</title>
      <dc:creator>Paul Singman</dc:creator>
      <pubDate>Wed, 12 Aug 2020 15:23:24 +0000</pubDate>
      <link>https://forem.com/aws-builders/why-you-should-never-ever-print-in-a-lambda-function-3i37</link>
      <guid>https://forem.com/aws-builders/why-you-should-never-ever-print-in-a-lambda-function-3i37</guid>
      <description>&lt;h4&gt;
  
  
  How to spot an AWS Lambda novice, just like &lt;em&gt;that&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Note: &lt;a href="https://towardsdatascience.com/why-you-should-never-ever-print-in-a-lambda-function-f997d684a705" rel="noopener noreferrer"&gt;This article&lt;/a&gt; was originally published in the Towards Data Science Medium Publication on Aug. 4th.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Tale of Two Lambda Users
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tale #1: The Amateur
&lt;/h3&gt;

&lt;p&gt;One moment everything is fine, then … Bam! Your Lambda function raises an exception, you get alerted and everything changes instantly.&lt;/p&gt;

&lt;p&gt;Critical systems could be impacted, so it’s important that you understand the root cause quickly.&lt;/p&gt;

&lt;p&gt;Now, this isn’t some low-volume Lambda where you can scroll through CloudWatch Logs and find the error in purely manual fashion. So instead you head to CloudWatch Insights to run a query on the log group, filtering for the error:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fmnibppxh9sc3hgmoishu.jpeg" class="article-body-image-wrapper"&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%2Fi%2Fmnibppxh9sc3hgmoishu.jpeg" alt="CloudWatch Insights query filtering on errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like we found our error! While helpful, unfortunately it omits any other log messages associated with the failed invocation.&lt;/p&gt;

&lt;p&gt;With just the information shown above maybe — just maybe — you can figure out what the root cause is. But more likely than not, you won’t be confident.&lt;/p&gt;

&lt;p&gt;Do you tell people you aren’t sure what happened and that you’ll spend more time investigating if the issue happens again? As if!&lt;/p&gt;

&lt;p&gt;So instead you head to the CloudWatch Logs Log Stream, filter records down to the relevant timestamp, and begin manually scrolling through log messages to find the full details on the specific errored invocation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution Time:&lt;/strong&gt; 1–2 hours&lt;br&gt;
&lt;strong&gt;Lambda Enjoyment Usage Index:&lt;/strong&gt; Low&lt;/p&gt;
&lt;h3&gt;
  
  
  Tale #2: The Professional
&lt;/h3&gt;

&lt;p&gt;Same Lambda function, same error. But this time the logging and error handling are improved. As the title implies, this involves replacing &lt;code&gt;print()&lt;/code&gt; statements with something a ‘lil better.&lt;/p&gt;

&lt;p&gt;What is that something and what does this Lambda function look like anyway? Let’s first go through what error debugging looks like for the professional, then take a look at code. Fair?&lt;/p&gt;

&lt;p&gt;Again, we start with an Insights query:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fckr4fucyraz5l5hdf271.jpeg" class="article-body-image-wrapper"&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%2Fi%2Fckr4fucyraz5l5hdf271.jpeg" alt="CloudWatch Insights query, again, filtering on errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And again we find the error in the logs, but unlike last time, the log event now includes the &lt;code&gt;@requestId&lt;/code&gt; from the Lambda invocation. What this allows us to do is run a second Insights query, filtered on that requestId to see the full set of logs for the exact invocation we are interested in!&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2Fpg4246nbeilfedgjqu7g.png" class="article-body-image-wrapper"&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%2Fi%2Fpg4246nbeilfedgjqu7g.png" alt="CloudWatch Insights query filtering on @requestId"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we get 5 results, which together paint the full crime scene picture of what happened for this request. Most helpfully, we immediately see the exact input passed to trigger the Lambda. From this we can either deduce what happened mentally, or run the Lambda code locally with the exact same input event to debug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution Time:&lt;/strong&gt; 10–20 minutes&lt;br&gt;
&lt;strong&gt;Lambda Enjoyment Usage Index:&lt;/strong&gt; High!&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code Reveal
&lt;/h2&gt;

&lt;p&gt;I’d like to imagine my readers are on the edge of their seats, begging to know the difference between the Amateur and the Pro’s code from the tale above.&lt;/p&gt;

&lt;p&gt;Whether that’s true or not, here is the Amateur Lambda:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;It is, of course, intentionally simple for illustrative purposes. Errors were generated by simply passing an event dictionary without artist as a key, for example: &lt;code&gt;event = {'artisans': 'Leonardo Da Vinci'}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now for the Professional Lambda, which performs the same basic function but improves upon the print() statements and error handling.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Interesting! So why are we using the logging module and formatting exception tracebacks?&lt;/p&gt;

&lt;h3&gt;
  
  
  Lovely Lambda Logging
&lt;/h3&gt;

&lt;p&gt;First, the Lambda runtime environment for python includes a &lt;a href="https://towardsdatascience.com/why-you-should-never-ever-print-in-a-lambda-function-f997d684a705" rel="noopener noreferrer"&gt;customized logger&lt;/a&gt; that is smart to take advantage of.&lt;/p&gt;

&lt;p&gt;It features &lt;a href="https://www.denialof.services/lambda/" rel="noopener noreferrer"&gt;a formatter&lt;/a&gt; that, by default, includes the &lt;code&gt;aws_request_id&lt;/code&gt; in every log message. This is the critical feature that allows for an Insights query, like the one shown above that filters on an individual &lt;code&gt;@requestId&lt;/code&gt;, to show the full details of one Lambda invocation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exceptional Exception Handling
&lt;/h3&gt;

&lt;p&gt;Next, you are probably noticing the fancy error handling. Although intimidating looking, using &lt;code&gt;sys.exec_info&lt;/code&gt; is the &lt;a href="https://docs.python.org/3/library/logging.html#logging.Logger.debug" rel="noopener noreferrer"&gt;standard way&lt;/a&gt; to retrieve information about an exception in python.&lt;/p&gt;

&lt;p&gt;After retrieving the exception name, value, and stacktrace , we format it into a json-dumped string so all three appear in one log message, with the keys automatically parsed into fields. This also makes it easy to create custom metrics based off specific errors without requiring complex string parsing.&lt;/p&gt;

&lt;p&gt;Lastly, it is worth noting that in contrast, logging an exception with the default Lambda logger without any formatting results in an unfortunate multi-line traceback looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fi%2F1okffr9q9vwpsrn5q1ni.png" class="article-body-image-wrapper"&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%2Fi%2F1okffr9q9vwpsrn5q1ni.png" alt="Multi-line exception traceback example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I hope if your Lambda functions look more like the Amateur Lambda at the moment, this article inspires you to upgrade your dance and go Pro!&lt;/p&gt;

&lt;p&gt;Before I let you go, I should warn that the downside to replacing print statements with proper logging is that you lose any terminal output generated from local executions of your Lambda.&lt;/p&gt;

&lt;p&gt;There are clever ways around this involving either environment variables or some setup code in a &lt;code&gt;lambda_invoke_local.py&lt;/code&gt; type of file. If interested, let me know and I’ll be happy to go over the details in a future article.&lt;/p&gt;

&lt;p&gt;Lastly, as a final bit of inspiration, instead of needing to run Cloudwatch Insights queries to debug yourself, it should be possible to set up an Alarm against the Lambda Errors metric that notifies an SNS topic when in state “In Alarm”. Another Lambda could then trigger off that SNS topic to run the same debugging Insights queries as the Pro automatically, and return the relevant logs in Slack or something.&lt;/p&gt;

&lt;p&gt;Would be cool, no?&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>python</category>
    </item>
  </channel>
</rss>
