Photo by Joanna Kosinska on Unsplash

Serverless local logging 🚀

Practical logging when running serverless apps locally.

Serverless Advocate
Level Up Coding
Published in
5 min readMay 30, 2021

--

When working with frameworks such as Serverless Offline, one of the biggest pain points in my opinion is getting access to the equivalent CloudWatch logs which would be generated in the cloud 🔥. This article shows you how too easily emulate CloudWatch logs when running locally with Serverless Offline in the most efficient way.

Optimal solution! ✔️

When I originally set out to tackle this problem, I thought about exactly what I wanted to achieve:

  1. Access: Have access to the logs, not just through the standard terminal output, but physical logs I can check when running dev testing or basic load tests (for example using artillery), just like I would have with CloudWatch. I wanted to be able to read, export, and manipulate at my leisure if I needed too, as well as referring back too whilst in development..🤔
  2. Pretty: I wanted the logs to be pretty! Nothing worse than black and white verbose mundane logs! 🙈
  3. Only Local: I only wanted this to affect running serverless locally, not when deployed to the cloud. In the cloud CloudWatch is King on AWS, and I don’t need to write additional physical logs. 👑
  4. DRY: I wanted to be able to use this solution across multiple projects. (DRY). 👊🏼

The solution….a custom logger! ✏️

My solution to this issue was to create a custom logger that would:

  1. Offline Only: Log locally to file when in offline mode only.
  2. Split Logs: I wanted to split the logs into ‘error only’ and ‘all’ combined logs for easy viewing.
  3. Colourful: I wanted the logs to be colourful and easy to read, both in the physical logs, as well as in the terminal output.
  4. Debug logs: I wanted to also log debug logs locally, but not when deployed to the cloud.
  5. Testing: I use Jest for my TDD/BDD/integration tests, and don’t want to log to the terminal or physical disk when running tests, but in all other scenarios locally I want this logged to disk and terminal.

With this in mind I created a custom logger package using Winston which when ran in offline mode would log locally to disk (both all and error only, including debug logs), and when deployed to AWS would only log to CloudWatch as normal. Simple! 🤘

A quick example..

Below is a very basic example of how you can achieve this. Personally I love Monorepo’s through Lerna or Nx, so it made sense to have a reusable logging package which can be used across all services rather than publishing this and pulling it into every repo through npm (although that is obviously the alternative!)

Note: when creating an actual production package you would allow for changing the folder destination, log names etc through config.

Example basic Typescript code for the logger.

This means that when your run serverless offline the process.env.IS_OFFLINE environment variable is already set for this to work nicely, so we can distinguish between the context of running in the cloud or through Serverless Offline.

The logs in offline mode are then written to local disk as shown here in a logs folder (separated out nicely).

Example local logging folder and logs split between all (combined) and error only.

The output to the local logs when using this custom logger and running serverless offline is colourful, easy to read, colour coded to log level, timestamped, split out logs, which also includes debug logging. Each lambda has its own logs too as you would have with CloudWatch.

Example logging output when using the custom logger.

In the terminal the logs are also formatted in a similar way and perfectly colour coded and easy to read, with both types of logs also including timestamps:

Serverless Offline terminal output with the custom logger.

When running tests through Jest I don’t want to see any output in the terminal from logging other than the Jest test passes and failures, so in this scenario I add a __mocks__ folder alongside the node_modules which has the following name and code:

/__mocks__/winston.ts

Basic example mocking of the Winston package using Jest.

This means that the underlying Winston package in this scenario is mocked with empty jest functions when running the tests only, so we override the functionality, and don’t display any logs in the Jest test run output or write to disk.

Wrapping up

I hope you found the basic example useful and to build upon!

Let's connect on any of the following:

https://www.linkedin.com/in/lee-james-gilmore/
https://twitter.com/LeeJamesGilmore

If you found the article useful please feel free to support me with a virtual coffee https://www.buymeacoffee.com/leegilmore and either way lets connect and chat! ☕️

If you enjoyed the post please follow my profile Lee James Gilmore for further posts/series, and don’t forget to connect and say Hi 👋

This article is sponsored by Sedai.io

About me

Hi, I’m Lee, an AWS certified Solutions Architect and polyglot software engineer based in the UK, working as a Cloud Architect and Principal Developer, having worked primarily in full-stack JavaScript on AWS for the past 5 years.

I consider myself a serverless evangelist with a love of all things AWS, innovation, software architecture and technology.

** The information provided are my own personal views and I accept no responsibility on the use of the information.

--

--

Global Head of Technology & Architecture | Serverless Advocate | Mentor | Blogger | AWS x 7 Certified 🚀