Best Practice Factory Modules
Microservice architectures offer the advantage of allowing multiple teams to work on different parts of a system simultaneously, thus reducing conflicts. However, this model presents its own set of challenges. A common issue is the proliferation of third-party libraries for tasks such as logging, HTTP requests, messaging and persistence. While standardising on a single library for each function is an important step, it is insufficient to address all concerns. Consider logging: Imagine if you standardised on pino, installing it directly in over 100 services, only then to find your logs include sensitive HTTP headers; to fix the problem at source you would have to add the redaction code to each one of those repositories.
A better approach is to wrap 3rd party libraries like pino in custom factory module that applies a set of tested, sensible conventions, safeguards and features, thus creating a pit of success. For a logging library, a good set of best practices are:
- A conventional API, i.e.
logger.<level>(<message>, [<context>])
- Support for machine friendly logging
- Support for human friendly logging
- Support for test friendly logging
- Support for async context tracking
- Support for redaction of sensitive content
- Reporting the source of empty log records
- Reporting the source of overised log records
- Relocating the context to a subdocument to avoid name clashes (level, time, etc)
- Ensuring dates are serialised correctly
- Ensuring errors are serialised correctly (pino#862, winston#1338, bunyan#514)
- Ensuring circular references are tolerated (pino#990, winston#1946, bunyan#427)
- Ensuring unserialisable context objects are tolerated
The above can all be achieved using most popular logging libraries, but only after significant customisation. Duplicating this work in each one of your services would be impractical and requires your team priortise it above more pressing tasks. As Jeff Bezos is reportedly fond of saying, Good intentions don’t work, good mechanisms do. Factory modules are a mechanism that enables best practice and leverages the hard won lessons you and your colleagues have accumulated over their careers.
You can find an example factory module for pino that implements the above best practices here.