3. Crazy geek


The original:
https://www.twilio.com/blog/g…


This article first send WeChat messages public number: front-end pioneer welcome attention, every day to you push fresh front-end technology articles


When you start developing in JavaScript, one of the first things you’ll probably learn is how to log content to the console using console.log. If you search for how to debug JavaScript, you’ll find hundreds of blog posts and StackOverflow articles that simply tell you to use console.log. Because this is such a common practice, we even use linter rules like no-console in our code to ensure that we don’t leave unexpected log information. But what if we really wanted to record something?

In this article, we’ll walk through the logging information to be logged in various situations, what is the difference between console.log and console.error in Node.js, and how to get logging from your library out to the user console without any confusion.

console.log(`Let's go! `);

Theory First: Important Details of Node.js

While you can use console.log or console.error in the browser and in Node.js, there is one important thing to keep in mind when using Node.js. When you write the following code in Node.js to a file named index.js:

console.log('Hello there');
console.error('Bye bye');

Execute it on a terminal with Node Index.js, and you’ll see the output of both:

While they may look the same, they are actually treated differently by the system. If you look in the console section of the Node.js documentation, you’ll see that console.log is printed to stdout and console. error uses stderr.

Each process has three default streams available. Those are stdin, stdout and stderr. The STDIN stream is used to process input from the process. Such as pressing a button or redirecting the output. The STDOUT stream is used for the output of the program. Finally, stderr is used for error messages. If you want to know why stderr exists and when you should use it, check out this article.

In short, this allows us to use in shell redirection (>) and a pipe (|) to handle errors and diagnostic information, they are with the results of the actual output of the program are separated. While > allows us to redirect the output of a command to a file, 2> allows us to redirect the output of stderr to a file. For example, the following command passes “Hello there” to a file named hello.log and “Bye Bye” to a file named error.log.

node index.js > hello.log 2> error.log

When should I log it?

Now that we’ve looked at the underlying techniques of logging, let’s talk about when you should log your content. Usually it should be one of the following:

  • Quick debugging of unexpected behavior during development
  • Browser-based analysis or diagnostic logging
  • Log incoming requests to your server, along with any possible failures
  • Use the library’s logging debugging options to help users resolve problems
  • Output progress, confirmation messages, or error messages to the CLI

We will skip the first two cases and focus on the last three based on Node.js.

Server program log

There may be several reasons why you log on the server. For example, it logs incoming requests and allows you to extract things like statistics, such as how many users get 404 errors when clicking, or the user-agent of the User’s browser. You also want to know when and for what went wrong.

If you want to try the following code, create a new project directory first. Create an index.js in the directory and run the following command to initialize the project and install Express:

npm init -y
npm install express

Let’s set up a server with middleware and output each request only with console.log. Copy the following into the index.js file:

const express = require('express');

const PORT = process.env.PORT || 3000;
const app = express();

app.use((req, res, next) => {
 console.log('%O', req);
 next();
});

app.get('/', (req, res) => {
 res.send('Hello World');
});

app.listen(PORT, () => {
 console.log('Server running on port %d', PORT);
});

Use console.log(‘%O’, req) here to log the entire object. Console. log uses util.format at the bottom to support % O placeholders. You can check out their details in the Node.js documentation.

When you run Node Index.js to start your server and navigate to http://localhost:3000, it prints out a lot of information that you really need but don’t know.

If we change it to console.log(‘%s’, req) and don’t print the whole object, we won’t get any more information.

! [the output of the terminal “[object object]” information] (https://s3.amazonaws.com/com…

We can write our own logging functions to output only the things we care about, but wait, let’s talk about the things we care about in general. While this information is often the focus of our attention, we may actually need additional information:

  • Timestamp – Know when something is happening
  • Computer/server name – if you are running a distributed system
  • Process ID – If you are using PM2 to run multiple Node processes
  • Message – the actual message that contains some content
  • Other variables or information that may be required

Since everything will be rolled over to stdout and stderr, we might want different logging levels, as well as the ability to configure and filter logs.

We can get all of this by relying on various parts of Process and writing a bunch of JavaScript, but the good news about Node.js is that there is an ecosystem of NPM, which already has a variety of libraries for us to work with. Some of them are:

  • pino
  • winston
  • roarr
  • bunyan(Please note that this has not been updated for 2 years)

I prefer Pino because it’s very fast. Let’s look at how Pino can be used to help us with logging. Requests can also be logged using the Express-Pino – Logger package.

Install Pino and Express-pino-logger:

npm install pino express-pino-logger

Update your index.js file to use logger and middleware with the following code:

const express = require('express');
const pino = require('pino');
const expressPino = require('express-pino-logger');

const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
const expressLogger = expressPino({ logger });

const PORT = process.env.PORT || 3000;
const app = express();

app.use(expressLogger);

app.get('/', (req, res) => {
 logger.debug('Calling res.send');
 res.send('Hello World');
});

app.listen(PORT, () => {
 logger.info('Server running on port %d', PORT);
});

In this code, we create an instance logger of Pino and pass it to Express-pino-logger to create a new logger middleware to invoke app.use. In addition, we replaced the console.log when the server started with logger.info and added an additional logger.debug to the route to show different logging levels.

Run Node Index.js again and restart the server, and you’ll see a completely different output, one JSON per line. Navigate to http://localhost:3000 again, and you’ll see that another line of JSON has been added.

If you examine the JSON, you’ll see that it contains all the information mentioned earlier, such as timestamps, and so on. You may also notice that the logger.debug statement is not printed. That’s because we have to change the default log level to see it. When we create the logger instance, we set the value to process.env.log_level, which means we can change the value through it or accept the default INFO. You can adjust the log level by executing LOG_LEVEL = debug node index.js.

Before we do that, we need to solve the problem that the current output is unfit for human reading. Pino follow a philosophy, in order to improve the performance, you should go through the pipe (|) move the output of any processing in a separate process. This includes making it readable or uploading it to the cloud host. These are called transports. You can see why errors in Pino are not written to stderr by looking at the Transports documentation.

Let’s use the tool PINO-Pretty to view the more readable version of the log. Run on your terminal:

npm install --save-dev pino-pretty
LOG_LEVEL=debug node index.js | ./node_modules/.bin/pino-pretty

Now all of the log by using the | operator input to pino – pretty command, pass through your output should be beautification, and also contains some key information, and should be in colour. If you request http://localhost:3000 again, you should also see the Debug message.

There are various transports available to beautify or transform your logs. You can even use pino-colada for emoji. These are useful for your local development. After running the server in production, you may want to transfer the logs to another Transports and write them to disk with > or with a command like tee for later processing.

This document will also contain information about rotating log files, filtering, and writing logs to different files.

Library of log

Now that we’re talking about logging effectively for our server program, why not use the same technique for our library?

The problem is that your library may want to debug by logging, but it should not be confused with the user’s program. If you need to debug something, the consumer should be able to enable logging. By default, your library should be silent and leave the decision of whether to output logs to the user.

A good example is Express. There’s a lot of stuff underneath Express that you might want to peek at while debugging your own programs. If we look at the express documentation, we’ll notice that you can add DEBUG=express:* before your command, as follows:

DEBUG=express:* node index.js

If you run this command, you’ll see a lot of other output that will help you debug problems in your program.

If you do not enable debug logging, you will not see any such logging. This is done through a package called Debug. It allows us to write log messages under the “namespace,” which will be printed if the user of the library includes the namespace or matches its wildcard in the DEBUG environment variable. To use the Debug library, first install it:

npm install debug

Let’s emulate our library by creating a new file called random-id.js and copying the following code into it:

const debug = require('debug');

const log = debug('mylib:randomid');

log('Library loaded');

function getRandomId() {
 log('Computing random ID');
 const outcome = Math.random()
   .toString(36)
   .substr(2);
 log('Random ID is "%s"', outcome);
 return outcome;
}

module.exports = { getRandomId };

This will create a new Debug logger with the namespace mylib:randomid, and then output two messages to the log. Let’s use it in index.js above:

const express = require('express');
const pino = require('pino');
const expressPino = require('express-pino-logger');

const randomId = require('./random-id');

const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
const expressLogger = expressPino({ logger });

const PORT = process.env.PORT || 3000;
const app = express();

app.use(expressLogger);

app.get('/', (req, res) => {
 logger.debug('Calling res.send');
 const id = randomId.getRandomId();
 res.send(`Hello World [${id}]`);
});

app.listen(PORT, () => {
 logger.info('Server running on port %d', PORT);
});

If we rerun our server with DEBUG=mylib:randomid node index.js, it will print the DEBUG log of the previous “library”.

If users of your library want to put this debugging information into their Pino logs, they can format these logs correctly using a library called PINO-DEBUG developed by the Pino team.

Install the library with the following command:

npm install pino-debug

Before we first use DEBUG, we need to initialize PINO-DEBUG. The easiest way to do this is to require modules using the Node.js -r or –require flag before launching the JavaScript script command. Rerun your server with the following command (assuming you have pino-colada installed) :

DEBUG=mylib:randomid node -r pino-debug index.js | ./node_modules/.bin/pino-colada

You will now view the library’s debug log in the same format as the program log.

CLI output

The final case covered in this article is a special case of logging against the CLI. My idea is to separate the “logical log” from the CLI’s output “log.” For all logical logs, you should use a library like Debug. This way, you or someone else can reuse the logic without being bound by the CLI’s specific use cases.

When you build the CLI with Node.js, you may want to add some nice looking colors, or format the information in a visually appealing way. However, here are a few things to keep in mind when building the CLI.

One scenario is that your CLI might be used in the context of a continuous integration (CI) system, so you might want to remove color and fancy decorative output. Some CI systems set an environment flag called CI. If you want to make it safer to check if you’re in CI, use a package like IS-CI to support a bunch of CI systems.

Libraries like Chalk have checked the CI for you and removed the colors for you. So let’s see what it looks like.

Install Chalk using NPM install Chalk and create a file named cli.js. Copy the following into it:

const chalk = require('chalk');

console.log('%s Hi there', chalk.cyan('INFO'));

Now if you would run this script using node cli.js you’ll see colored output. Now if you run the script with Node Cli. Js, you’ll see color output.

But if you run it with CI=true node cli.js, you’ll see the color erased:

Another scenario you need to keep in mind is whether stdout is running in terminal mode, that is, writing to the terminal. If that’s the case, we can use something like Boxen to display all the nice output. If not, the output might be redirected to a file or piped somewhere.

You can check if stdin, stdout, or stderr are in terminal mode by checking the isTTY property on the corresponding stream. For example, process.stdout.istty. TTY means “teletypewriter” and in this case is used exclusively for terminals.

Depending on how the Node.js process is started, each of these streams may have different values. You can read more about it in the “Process I/O” section of the Node.js documentation.

Let’s take a look at how the value of process.stdout.istty changes in different cases. First update your cli.js:

const chalk = require('chalk');

console.log(process.stdout.isTTY);
console.log('%s Hi there', chalk.cyan('INFO'));

Run Node Cli. Js in a terminal, and you’ll see that the output’s true is colored.

Then run the same content, but redirect the output to a file, and check the contents:

node cli.js > output.log
cat output.log

You’ll see that this time it prints undefined followed by a simple colorless message because the redirection of stdout turns off its terminal mode. Because Chalk uses SUPPORT-COLOR, they will check the isTTY on the corresponding stream.

Tools like Chalk already handle this behavior for you, but when developing a CLI, you should always be aware that the CLI might run in CI mode or redirect the output. It can also help you take the CLI experience a step further. For example, you can arrange data in a nice way in the terminal, and if isTTY is undefined, you can switch to a way that is easier to parse.

conclusion

Logging your first line with console.log is really quick when you first start JavaScript development, but you should think more about logging when you get your code into production. This article is purely an introduction to the various methods and available logging solutions. I encourage you to take a look at some of your favorite open source projects to see how they address logging issues and the tools they use.

If you know of or find a tool I didn’t mention, or if you have any questions, please leave a comment.


This article first send WeChat messages public number: front-end pioneer

Welcome to scan the two-dimensional code to pay attention to the public number, every day to push you fresh front-end technology articles


Read on for the other great articles in this column:

  • 12 Amazing CSS Experiment Projects
  • 50 React Interview Questions You Must Know
  • What are the front-end interview questions at the world’s top companies
  • 11 of the best JavaScript dynamic effects libraries
  • CSS Flexbox Visualization Manual
  • React from a designer’s point of view
  • The holidays are boring? Write a little brain game in JavaScript!
  • How does CSS sticky positioning work
  • A step-by-step guide to implementing animations using HTML5 SVG
  • Programmer 30 years old before the monthly salary is less than 30K, which way to go
  • 14 of the best JavaScript data visualization libraries
  • 8 top VS Code extensions for the front end
  • A complete guide to Node.js multithreading
  • Convert HTML to PDF 4 solutions and implementation

  • More articles…