开源日报 每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,坚持阅读《开源日报》,保持每日学习的好习惯。
今日推荐开源项目:《网易云音乐+命令行 musicbox》
今日推荐英文原文:《You Built Your Node App, But Are You Logging?》

今日推荐开源项目:《网易云音乐+命令行 musicbox》传送门:项目链接
推荐理由:这个项目成功用 Python 将有图形化界面的网易云音乐变成没有图形化界面的命令行程序,并且在功能上还没有做太多的阉割;尽管原来的网易云音乐也有快捷键功能,但是终究比不过专精此道的命令行程序,这个项目提供了比原版更加丰富的快捷键以方便用户的操作。
今日推荐英文原文:《You Built Your Node App, But Are You Logging?》作者:Juan Cruz Martinez
原文链接:https://medium.com/better-programming/you-built-your-node-app-but-are-you-logging-665c7b2bb06b
推荐理由:在创建 Node 应用时正确使用日志的姿势

You Built Your Node App, But Are You Logging?

Logging is a best practice. Here’s how to do it right

Logging is a crucial part of developing an application. When the app is in production, logs are necessary to identify the problem if something goes wrong. So if you are a developer, you should ask yourself this question: “Am I logging the right way?”

In this article, we are going to provide an answer to that question. We will discuss the best practices developers — especially Node.js developers — should follow when logging their application events.

Why Are Logs Important?

No matter how careful we are when developing an application, it’s a difficult task to make it 100% secure and bug-free. We try to find and solve most of the problems during development by testing and debugging. Still, we won’t be able to catch all of them.

Because of these remaining errors, applications running in production might behave in unexpected ways in certain situations. Sometimes, they can be really critical. They may even crash the application entirely. In such a case, can we run a debugger to figure out what went wrong with our application? No, it’s not the most practical idea.

Instead, we use application logs to understand how and why the application is behaving differently. For this, we have to set up our application to record information about its events and errors. And this is what we call logging. Logging helps us identify problems with an application running in production.

Logging Best Practices

Since logs are quite important, we need to follow logging practices that will help us easily identify problems and their causes.

1. Don’t use console.log

Developers tend to rely on Node’s console.log function to log application events since it’s easily accessible, needs no additional setup, and is simple to use. But if you want to take logging seriously — and you should — this is not the way to achieve it.

console.log prints its output to stdout. Other console functions, like console.err and console.warn, print outputs to stderr. You can’t configure console.log to transport the logs to a file or a database. You can’t turn logging on and off or switch between different logging levels (which we will talk about later) when the app is in production.

Simply put, console.log doesn’t provide enough features or configuration options to become an adequate logging tool. You should instead use a dedicated logging library to get the job done properly.

2. Use a dedicated logging library

A dedicated logging library, unlike console.log, provides a set of features to create logs that let us identify problems easily and enough configurations to make the best use of our logs.
  • Most logging libraries support several logging levels like info, debug, warning, and error. These levels help filter logs according to our needs.
  • The biggest advantage of using a logging library is being able to switch between logging levels even when the app is in production.
  • They also support formatting logs with different colors for different levels of logs. Some libraries also support the formatting of different data types like JSON.
Winston and Bunyan are two of the most popular logging libraries available to Node developers.

In this article, we are going to use Winston in the code examples.

3. Source, timestamp, context — the most important parts of a log

Every log recorded by your application should consist of these three parts.
  • Source: If we are debugging our application using the logs, it’s important to know where each event occurred. The source could be the name of the host, method, zone, or in microservices architecture, the name of the service.
  • Timestamp: Recording the timestamp of the events that occurred is also important to logging. We might need to filter the logs recorded within a certain timeframe or sort the logs by the time they occurred. Hence, the timestamp is an essential part of an application log.
  • Context and level: The context of a particular log is important when we are debugging the application. For example, if the application is registering a new user, there are several ways this operation could fail. The user might provide invalid data or could already be registered in the system. These failures are not occurring because our application is behaving faultily. But if this operation fails because the application couldn’t connect to the database, that signifies that something has gone wrong. Therefore, providing the context of the event with every log is crucial to make the best of logging. In addition, recording the level is also important to filter and identify different issues of the application based on how critical they are.

4. Use log levels properly

We use logging levels to sort them by urgency so that we can filter them accordingly.

Syslog standard provides specified levels, declared according to their severity, that we can use when logging.
  • Emergency: The system is unusable.
  • Alert: Action must be taken immediately.
  • Critical: Critical conditions.
  • Error: Error conditions.
  • Warning: Warning conditions.
  • Notice: Normal but significant conditions.
  • Informational: Informational messages.
  • Debug: Debug-level messages.
You can alter standard levels to create a list of levels that better suit your application. However, each log must be given a level to be able to filter them as required.

5. What not to do when logging

Logging should not generate any errors of its own.

We are trying to find errors in our application with the logs. We don’t need logs to add their own errors on top of that. So, make sure that logging operations are written in a way that does not generate errors of its own.

For example, the following code could throw an error when logging. You should avoid instances like this:
const logger = require("../logger")
exports.findUserByUsername = async (req, res) => {
    logger.info(`Invoking findUserById with the id ${req.params.username}`)
    //implementation
    logger.debug(`Finding user data from the database ${userModel.find({username: req.params.username})}`) //could throw an error.
}
Logging operations should also be stateless.

Logging operations should not generate any state changes in the application, like changing the database. You should avoid scenarios like this:
exports.saveUser = async (req, res) => {
    logger.info("invoking saveUser()")
    //implementation
    logger.debug(`saving user to the database ${userModel.save(req.body)}`) //changes application state
}

6. Use the appropriate logging level in production

Being able to log records of every level would be ideal when our app is in production. But it’s not always practical. If your application has heavy user traffic, logging every level of code would result in a huge performance dip.

We need to take a proactive approach to avoid this and log optimally. In a production-level application, the majority of the logs belong to the debug and info levels. So during the normal runtime, if we turn off debug and info logs, and log only the lower levels, we can avoid the performance issues that come with frequent logging.

In an application in production, we turn on the warning, error, and other lower levels of logs to identify if it is in critical status. We can turn on debug- and info-level logs only when an error is detected.

One of the benefits of using a logging framework is being able to change the logging level easily while in production.

7. Store the current logging level as an environment variable

To ensure that we can easily change between levels when needed, you should store the current logging level of the application as an environment variable. This gives us the ability to change the level when the application is still in production by simply changing the variable value.
//.en.
LOG_LEVEL = "warn"


//logger.js
const transports = {
  console: new winston.transports.Console({ level: process.env.LOG_LEVEL}),
};
const logger = winston.createLogger({
  transports: [
    Transports.console,
 ]
});

Summary

If you are building an application that is intended to go to production, logging is a crucial feature it should have. In this article, we discussed the best practices you should use when creating a logging system. With this knowledge, you can start building a great logging system for your application today.

Thanks for reading!
下载开源日报APP:https://openingsource.org/2579/
加入我们:https://openingsource.org/about/join/
关注我们:https://openingsource.org/about/love/