This project implements a production-ready logger for Nest.js applications using Winston and Morgan on a clean architecture (Hexagonal Architecture).
You can read a detailed explanation of the project on the following article: https://medium.com/p/d03e3bb56772/edit
That implements a production-ready system advanced or basic Microservices or Monoliths projects applying concepts like:
- Correlation IDs
- Decoupled log transporters
- Log levels
- Logging Rules
- Log formatters
The project is structured in a Nest.js monorepo.
The folder structure is as follows:
- apps: It contains the project's executable applications, in this case, the REST API.
- api: It contains the REST API developed with Nest.js.
- libs: It contains the project packages or Bounded Contexts on a DDD language.
- shared: It contains the code of the Shared Kernel.
- src: It contains the source code.
- config: It contains de config module.
- context: It contains the context module.
- logger: It contains the logger module.
- src: It contains the source code.
- shared: It contains the code of the Shared Kernel.
The project is structured on a Hexagonal Architecture, so it has the following layers:
- Application
- Domain
- Infrastructure
We use Winston to manage the logs and Morgan to log the HTTP requests. All that is managed by the LoggerModule
.
To manage Winston transports, we use the WinstonLoggerTransportsKey
DI token that is defined on the LoggerModule
.
To decouple the Logging library from our domain, we have created a Logger
interface that is implemented by the WinstonLogger
class.
If we want to change the logging library, we only have to implement the Logger
interface and update the dependecy on the LoggerModule
.
NestJS uses a custom logger for bootstrap and internal logging. To use our logger, we need to create an adapter that implements the NestJS LoggerService
interface. That is implemented in the NestjsLoggerServiceAdapter
class.
We pass that adapter to the NestJS app on the main.ts
file.
To manage correlation IDs, we use nestjs-cls
library that implements a Local Storage. With that, we can isolate and share data on a request lifecycle.
The system reads the x-correlation-id
HTTP header and stores it on the Local Storage. If the header is not present, the system generates a new UUID and stores it on the Local Storage.
To add custom data to all the logs, we use a wrapper LoggerContextWrapper
.
That class is injected with a Transient scope. By that, we can get the caller class and add it to the logs.
-
Install dependencies
yarn install
-
Copy file .env.example a .env
cp .env.example .env
-
Start REST API
yarn start:dev api
-
Test REST API
curl -X GET http://localhost:3000
-
Test REST API with correlation ID
curl -X GET http://localhost:3000 -H "x-correlation-id: 87815cc5-d0f2-41e5-a731-ac55bbb733e8"
We will continue working on this project to add new features
PRs are welcome!
- Add testing.