There is a new requirement that the server use Nodejs to type log files and capture request logs. And then I came up with log4js…

Without further ado, go to the code

Install log4js

npm install log4js --save
Copy the code

Unfamiliar with the log4js view document log4js;

Create log_config.js file in config folder:

var log4js = require("log4js");
var path = require("path");
var fs = require("fs");
var basePath = path.resolve(__dirname, ".. /logs");

var errorPath = basePath + "/errors/";
var resPath = basePath + "/responses/";

var errorFilename = errorPath + "/error";
var resFilename = resPath + "/response";

/** * determine if the directory exists and create a directory */ if it does not
var confirmPath = function(pathStr) {
  if(! fs.existsSync(pathStr)) { fs.mkdirSync(pathStr);console.log("createPath: "+ pathStr); }}; log4js.configure({appenders: {
    errorLog: {
      type: "dateFile".// Log type
      filename: errorFilename, // Log output position
      alwaysIncludePattern: true.// Is there always a suffix
      pattern: "-yyyy-MM-dd.log" // suffix, a new log file is created every hour
    },
    responseLog: {
      type: "dateFile".filename: resFilename,
      alwaysIncludePattern: true.pattern: "-yyyy-MM-dd.log"}},categories: {
    errorLog: { appenders: ['errorLog'].level: 'error' },
    responseLog: { appenders: ["responseLog"].level: "info" },
    default: { appenders: ['responseLog'.'errorLog',].level: 'trace'}},// pm2: true,
  // pm2InstanceVar: 'INSTANCE_ID',
  disableClustering: true
});
// create root directory 'logs'
if (basePath) {
  confirmPath(basePath);
  // Create different file directories based on different logtypes
  confirmPath(errorPath);
  confirmPath(resPath);
}

module.exports = log4js;
Copy the code

This code is explained in the official document configuration line;

Create a new log.js file under config:

var log4js = require("./log_config");

var errorLog = log4js.getLogger("errorLog"); // The category value is used here
var resLog = log4js.getLogger("responseLog"); // The category value is used here

var log = {};
log.i = function(req, resTime) {
  if(req) { resLog.info(formatRes(req, resTime)); }}; log.e =function(ctx, error, resTime) {
  if(ctx && error) { errorLog.error(formatError(ctx, error, resTime)); }};// Format the request log
var formatReqLog = function(req, resTime) {

  let getClientIp = function (req) {
    return req.headers['x-forwarded-for'] ||
      req.connection.remoteAddress ||
      req.socket.remoteAddress ||
      req.connection.socket.remoteAddress || ' ';
  };
  let ip = getClientIp(req).match(/\d+.\d+.\d+.\d+/);

  var logText = new String(a);// Access methods
  var method = req.method;
  logText += "request method: " + method + "\n";
  // Request the original address

  logText += "request originalUrl: " + req.originalUrl + "\n";
  // Client IP address
  logText += "request client ip: " + ip + "\n";
  
  // Request parameters
  if (method === "GET") {
    logText += "request query: " + JSON.stringify(req.query) + "\n";
  } else {
    logText += "request body: " + "\n" + JSON.stringify(req.body) + "\n";
  }

  // Server response time
  logText += "response time: " + resTime + "\n";

  return logText;
};

// Format the response log
var formatRes = function(res, resTime) {
  var logText = new String(a);// The response log starts
  logText += "\n" + "*************** response log start ***************" + "\n";

  // Add the request log
  logText += formatReqLog(res, resTime);

  // Response status code
  logText += "response status: " + res.res.statusCode + "\n";

  // Response content
  logText += "response body: " + "\n" + JSON.stringify(res.body) + "\n";

  // The response log ends
  logText += "*************** response log end ***************" + "\n";

  return logText;
};

// Format error log
var formatError = function(ctx, err, resTime) {
  var logText = new String(a);// Error message starts
  logText += "\n" + "*************** error log start ***************" + "\n";

  // Add the request log
  logText += formatReqLog(ctx, resTime);

  // Wrong name

  logText += "err name: " + err.name + "\n";
  // Error message

  logText += "err message: " + err.message + "\n";
  // Error details

  logText += "err stack: " + err.stack + "\n";

  // The error message ends
  logText += "*************** error log end ***************" + "\n";

  return logText;
};

module.exports = log;
Copy the code

I’m just going to write three functions here,

  1. Request log formatReqLog;
  2. Format response log formatRes;
  3. Log formatError;

Configure and use in app.js

const log = require("./config/log");
// logger
app.all("*".async (req, res, next) => {
  // Response start time
  const start = new Date(a);// Response interval time
  var ms;
  try {
    // Start going to the next middleware
    await next();
    // Record the response log
    ms = new Date() - start;
    log.i(req, ms);
  } catch (error) {
    // Record the exception log
    ms = new Date() - start;
    log.e(req, error, ms);
  }
  console.log(`${req.method} ${req.url} - ${ms}ms-${res.statusCode}`);
});
Copy the code

There’s nothing to say about the code, just use it, and I’m going to talk about potholes

Fault 1: Pm2 + Log4JS fails to start the project

Nodejs application Error: bind EADDRINUSE when use pm2 deploy

sudo lsof -i -P | grep 3000
Copy the code

To solve the problem stamp here;

Fault 2: Log4JS with pM2 cluster exception log

For details, see the following link: Log4JS with PM2 Cluster Exception log solution

Pm2 is started in cluster mode, resulting in abnormal output of log4JS logs. Are there two solutions?

  1. Simply add the following command to log_config.js
disableClustering: true
Copy the code

I. Install the PM2-Intercom interprocess communication module ii of the PM2. Then add the following command to log_config.js

pm2: true,
pm2InstanceVar: 'INSTANCE_ID'
Copy the code

Iii. The pm2. Json as follows

{ "apps": [ { "name": "testing", "script": "pm2.js", "instances": 0, "instance_var": "INSTANCE_ID", // add the line exec_mode: "cluster"}]}Copy the code

The difference between the two solutions is :(from the author of log4js, I am so happy to have his help 😊)

It is better if you don't need 'disableClustering' and 'pm2' in your config. 'pm2: true` turns on support for using pm2-intercom, `disableClustering: True 'turns off all clustering support so it won't use PM2-intercom. Use one or the other, but not both.Copy the code

When deployed to the server, logs can be output normally, as shown below

The final reference article is as follows:

Official document link

Pm2 Official document PM2 usage analysis

log4js

  1. Log4js translation;
  2. Log4js with pM2 cluster exception log solution
  3. PM2 & log4JS && Winston Log management
  4. Vim’s command;
  5. Log4js configuration;