Event-Driven Logging
Application logs, combined with traces (see the next section), are essential for debug‐ ging and troubleshooting issues, either in real time or retrospectively. However, logs are easily overused and overly relied upon in serverless applications. This can result in an exponential increase in the time and knowledge it takes to debug an issue, as well as high costs (see Chapter 9) and security concerns (see Chapter 4), such as leaking sensitive data and secrets.
In an event-driven system, a log should ideally be a record of the occurrence of an event and its payload. These logs are very useful and can be used as an accessible, short-lived mirror of your application’s event store, allowing you to see the events that have occurred for a particular user journey or transaction. If you are also using a standard event envelope, like CloudEvents, this ensures log data is consistent and queryable. Here’s an example of an event payload that adheres to the CloudEvents specification:
{
“data”: {
“countryCode”: “DK”,
“orderNumber”: “1234”
},
“datacontenttype”: “application/json”,
“id”: “5x6y7z8”,
“source”: “/myapp/public”,
“specversion”: “1.0”,
“subject”: “Order.Created”,
“time”: “2020-07-28T12:04:00Z”,
“type”: “com.myapp.order.created”
}
Efficiently aggregating and analyzing vast amounts of logs across multiple service contexts and Lambda functions requires consis‐ tency in the format and properties of a log. You can use Lambda Powertools (see Chapter 6) to enforce standard log metadata and can consider providing type definitions to engineers to restrict the attributes that can be included in log data.
Beyond these event-based logs, you should only use logging as a last resort in your observability stack. Whenever you feel the need to add a log to record activity or state, consider whether the same information can be captured in a custom metric or a trace. Generally, tracing is a much more powerful and useful method for understand‐ ing the behavior of your system.