Logging with AWS Lambda Powertools for TypeScript
Logging and tracing offer invaluable insights to the state of your application. Supercharge your Serverless logging with AWS Lambda Powertools for TypeScript by creating a single re-usable log instance using custom log formats driven by the runtime environment
This read will enable you to:
- Seamlessly integrate AWS Powertools logger using a one-liner middleware approach for all of your Lambda handlers
- Get highly detailed logs in AWS
- Get simple one-liner log entries for localhost development
- Capture custom fields specific to your logging and reporting needs
AWS Lambda Powertools for TypeScript is a long awaited package enabling TypeScript developers to use similar functionality as provided by the equivalent Python package 🤘
While the official guide offers a number of implementations, I'd like to share my approach using middleware and custom tailored log formats for AWS and localhost development respectively.
Reduce the clutter while capturing a high level of detail
On localhost using serverless-offline
plugin, I want a simple, flat and readable format with a minimum of noise:
{"logLevel":"INFO","message":"Invoking Lambda handler function","data":{"type":"user"}}
Nah. Too busy. Let's dumb it down further:
INFO Invoking Lambda handler function { "type":"user" }
That's it. A simple one-liner is what I need. That's simple enough to squeeze a cluster of these into multiple events and get a clear picture of what's going on:
INFO Invoking Lambda handler function { type: "user" }
DEBUG Updating user profile { lastLogin: '2022-11-23T05:01:30.051Z' }
ERROR Lambda execution failed: Can not read undefined of foo
While on AWS I'd expect a far greater level of detail:
We need all of these details to help troubleshoot, build dashboards and correlate events across multiple data sources.
I can't stress enough how important it is to capture correlation-id
since this will help you correlate logs from multiple data sources and follow a customer journey though multiple systems.
Let's build it!
Following the basic setup from the GitHub code repo, we're focusing on building:
- A handler function wrapped in Middy middleware
- A middleware function attaching the Lambda context to the logger
- A logger factory method returning a decorated
Logger
instance
Middified handler
Nothing fancy here. A plain handler wrapped in Middy using a single middleware function.
The middleware function
This middleware is attaching the Lambda application context to the logger instance (I'm aware that the library comes with a similar function).
Log service
Factory style function returning a singleton instance of the Logger
class. It's key that the instance is created outside of the function scope. This means the Logger
class is instantiated before the middleware and function handler are invoked.
Also, notice how the log format is driven by the runtime environment using the isAWS()
ternary expression:
Putting it all together
Now you can simply import the wrapped Powertools logger and use it anywhere in your source code:
import logger from '@/services/logger';
export const login = async(
email: string,
password: string,
): Promise<void> => {
logger.debug('Authenticating user', { data: { email } });
// Implementation
};
Happy coding.
Source code on GitHub