mirror of
https://github.com/goldbergyoni/nodebestpractices.git
synced 2025-10-29 16:46:01 +08:00
updates based on feedback
This commit is contained in:
@ -651,9 +651,9 @@ All statements above will return false if used with `===`
|
||||
|
||||
<br/><br/>
|
||||
|
||||
## ![✔] 5.18. Log Routing
|
||||
## ![✔] 5.18. Don't route logs within the app
|
||||
|
||||
**TL;DR:** Logs should be routed not by the application, but by the exection environment the application runs in. Node applications typically write logs to `stdout` using the `console` module. Instead of the application specifying where the logs should end up, write to `stdout` using `console` and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, etc.).
|
||||
**TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developer should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.).
|
||||
|
||||
**Otherwise:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns
|
||||
|
||||
|
||||
@ -16,8 +16,7 @@ We all love console.log but obviously, a reputable and persistent logger like [W
|
||||
var logger = new winston.Logger({
|
||||
level: 'info',
|
||||
transports: [
|
||||
new (winston.transports.Console)(),
|
||||
new (winston.transports.File)({ filename: 'somefile.log' })
|
||||
new (winston.transports.Console)()
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@ -4,21 +4,13 @@
|
||||
|
||||
### One Paragraph Explainer
|
||||
|
||||
Application code should not handle log routing, but instead just use the `console` module to write to `stdout/stderr`.“Log routing” means picking up and pushing logs to a some other location than your application or application process, for example, writing the logs to a file, database, etc. The reason for this is mostly two-fold: 1) separation of concerns and 2) [12-Factor best practices for modern applications](https://12factor.net/logs).
|
||||
Application code should not handle log routing, but instead should use a logger utility to write to `stdout/stderr`. “Log routing” means picking up and pushing logs to a some other location than your application or application process, for example, writing the logs to a file, database, etc. The reason for this is mostly two-fold: 1) separation of concerns and 2) [12-Factor best practices for modern applications](https://12factor.net/logs).
|
||||
|
||||
We often think of "separation of concerns" in terms of pieces of code between services and between services themselves, but this applies to the more “infrastructural” components as well. Your application code should not handle something that should be handled by infrastructure/the execution environment (most often these days, containers). What happens if you define the log locations in your application, but later you need to change that location? That results in a code change and deployment. Also, when working with container-based/cloud-based platforms, containers can spin up and shut down when scaling to performance demands, so we can't be sure where a logfile will end up. The execution environment (container) should decide where the log files get routed to instead. The application should just log what it needs to to `stdout` / `stderr`, and the execution environment should be configured to pick up the log stream from there and route it to where it needs to go.
|
||||
|
||||
Benefits:
|
||||
- keeping the log routing responsibility out of your application code:
|
||||
- keeps the code cleaner
|
||||
- makes log routing locations easier to change without deployments
|
||||
- scaling applications/containers means it’s harder to have control over logfiles
|
||||
- scaling applications also means they’re more ephemeral, meaning logfiles might not be there depending on the state of the container
|
||||
- writing to, say a file or database, over stdout/stderr ties you down to those log targets, takes away your flexibility to pipe the output of stdout/stderr to whatever targets you want, and change this at the infrastructure or container level
|
||||
We often think of "separation of concerns" in terms of pieces of code between services and between services themselves, but this applies to the more “infrastructural” components as well. Your application code should not handle something that should be handled by infrastructure/the execution environment (most often these days, containers). What happens if you define the log locations in your application, but later you need to change that location? That results in a code change and deployment. When working with container-based/cloud-based platforms, containers can spin up and shut down when scaling to performance demands, so we can't be sure where a logfile will end up. The execution environment (container) should decide where the log files get routed to instead. The application should just log what it needs to to `stdout` / `stderr`, and the execution environment should be configured to pick up the log stream from there and route it to where it needs to go. Also, those on the team who need to specify and/or change the log destinations are often not application developers but are part of DevOps, and they might not have familiarity with the application code. This prevents them from easily making changes.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Code Example – Log routing tightly coupled to application
|
||||
### Code Example – Anti-pattern: Log routing tightly coupled to application
|
||||
|
||||
```javascript
|
||||
const { createLogger, transports, winston } = require('winston');
|
||||
@ -45,7 +37,14 @@ Doing it this way, the application now handles both application/business logic A
|
||||
### Code Example – Better log handling + Docker example
|
||||
In the application:
|
||||
```javascript
|
||||
console.log('something we want to log here') // this writes to stdout
|
||||
const logger = new winston.Logger({
|
||||
level: 'info',
|
||||
transports: [
|
||||
new (winston.transports.Console)()
|
||||
]
|
||||
});
|
||||
|
||||
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });
|
||||
```
|
||||
Then, in the docker container `daemon.json`:
|
||||
```javascript
|
||||
@ -67,6 +66,15 @@ So this example ends up looking like `log -> stdout -> Docker container -> Splun
|
||||
From the [O'Reilly blog](https://www.oreilly.com/ideas/a-cloud-native-approach-to-logs),
|
||||
> When you have a fixed number of instances on a fixed number of servers, storing logs on disk seems to make sense. However, when your application can dynamically go from 1 running instance to 100, and you have no idea where those instances are running, you need your cloud provider to deal with aggregating those logs on your behalf.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Quote: "12-Factor"
|
||||
|
||||
From the [12-Factor best practices for logging](https://12factor.net/logs),
|
||||
> A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout.
|
||||
|
||||
> In staging or production deploys, each process’ stream will be captured by the execution environment, collated together with all other streams from the app, and routed to one or more final destinations for viewing and long-term archival. These archival destinations are not visible to or configurable by the app, and instead are completely managed by the execution environment.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Example: Architecture overview using Docker and Splunk as an example
|
||||
|
||||
Reference in New Issue
Block a user