Logging can provide us with the necessary information about the system’s behavior to locate online code problems without getting lost at 🤠. This is why logging is important, as it is for Node applications. There are a variety of logging tools, including Log4js, Bunyan, Winston, Today we will talk about Winston who has the most stars on GitHub

Installation needless to say, is the following code

npm install winston

The basic use

const logger = require('winston')
logger.info('is info') Logger. log('info', 'is info')
logger.warn('is warn') // logger.log('warn', 'is warn')
logger.error('is error') // logger.log('error', 'is error')
Copy the code

The log is printed to the console

[winston] Attempt to write logs with no transports {"message":"is info"."level":"info"}
[winston] Attempt to write logs with no transports {"message":"is warn"."level":"warn"}
[winston] Attempt to write logs with no transports {"message":"is error"."level":"error"}
Copy the code

Storage/Output mechanism (TranSports)

Built-in transports

Sometimes we want to receive logs on the console and save them to a file

Pm2 out.log to log the students above code can be achieved, and there is a level classification

If you use docker and other non-PM2 startup options, it is also very simple, such as saving to the specified server.log, just use the following code

const winston = require('winston')
const path = require('path')
const logger = winston.createLogger({
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({filename: path.resolve(__dirname, '.. /logs/server.log')})
    ]
})
logger.info('print to the console and the file')
Copy the code

The above is using the Winston built-in storage/output mechanism (TranSports) :

  • Console: Console transmission
  • File: indicates File transfer

In addition to the built-in TranSports

  • Http:httptransmission
  • Stream: indicates Stream transmission

Custom transports

You can write your own log method that inherits from the Winston.transport class and implements it

const Transport = require('winston-transport');
const util = require('util');

module.exports = class YourCustomTransport extends Transport {
    constructor(opts) {
        super(opts);
    }

    log(info, callback) {
        setImmediate(() = > {
            this.emit('logged', info); }); callback(); }};Copy the code

Log files are sliced (flipped) by time

Install the log rollover component

npm install winston-daily-rotate-file

const dailyRotateFile = require('winston-daily-rotate-file');
const dateTransport = new dailyRotateFile({
    filename: path.resolve(__dirname, './logs/server.log'),
    maxSize: '50m'.createSymlink: true.symlinkName: 'server.log'
})
const logger = winston.createLogger({
    transports: [
        dateTransport
    ]
});
Copy the code

MaxSize is the maximum size of the file after rotation. The units are K (KB), m(MB), g(GB).

You get the file server.log.2021.01.28

Can be obtained when the log file size is greater than maxSize

Server.log.2021.01.28.1, server.log.2021.01.28.2, server.log.2021.01.29…

When createSymlink is true, a symbolic link is created to the current active log file from the specified name (symlinkName)

Convenient ELK scanning

Log desensitization

In order to comply with the national protection level 3 regulations, the user privacy information in the log file must be encrypted, such as mobile phone number, ID card and so on

Use Winston’s custom formatting capabilities

That is, custom format is used at createLogger time

const { createLogger, format } = require('winston');
const util = require('util')
// Customize the format
const formatLog = format.printf(info= > {
    // Logger. info('is info') info.message is info
    const msg = info.message;
    // logger.info('my name is %s, my personal information is %s', 'xiaolin',{ name: 'xiaolin', phone: '15811111111' }) 
    // info[SPLAT] = 'xiaolin' and {name: 'xiaolin', phone: '15811111111'
    if (typeof (msg) === 'string' && msg.includes('%s')) {// Compatible with old logs (format)
        const splat = info[SPLAT] || info.splat || [];
        // Desensitize the object
        const splatInfo = splat.map(item= > {
            if (typeof item === 'object') {
                // encryptionLog (encryptionLog) encryptionLog (encryptionLog) // encryptionLog (encryptionLog
                return util.inspect(encryptionLog(item), false.null)}returnitem }) info.message = util.format(msg, ... splatInfo);// String interpolation
    }
    // Info.timestamp is the value taken by the built-in formatting plugin format.timestamp
    const finalLog = ` [${info.timestamp}] [${info.level}] ${info.message}`
    return finalLog
})

exports.businessLog = createLogger({
    format: format.combine(
        format.timestamp({
            format: 'YYYY-MM-DD HH:mm:ss'
        }),
        formatLog
    )
});
Copy the code

Attached is desensitization I wrote myself

Here is my own simple desensitization rules, that is, to log in the object for desensitization

const encryRules = require('./encryRules').encryRules
/** * log desensitization for deep copy */ PS: desensitization for deep copy */
const encryptionLog = (splat, keyName) = > {
    if (splat === null) return splat;
    if (splat instanceof Date) return new Date(splat);
    if (splat instanceof RegExp) return new RegExp(splat);
    if (typeofsplat ! = ="object") {
        if (encryRules.rules.hasOwnProperty(keyName)) {
            // Match rules for regular desensitization
            return encryRules.rules[keyName](splat)
        }
        return splat
    };
    let cloneSplat = new splat.constructor();
    for (let key in splat) {
        if (splat.hasOwnProperty(key)) {
            // Recursive copycloneSplat[key] = encryptionLog(splat[key], key); }}return cloneSplat;
}
Copy the code

Encryption Rules (encryrules.js)

const regularEncrypt = (str, ruleName) = > {
    if(str ! =null&& str ! =undefined) {
        switch (ruleName) {
            case 'phone':// phone
                return String(str).replace(/(\d{3})\d{4}(\d{4})/g.'$1 $2 * * * *')
            case 'email':/ / email
                return str.replace(/ / / ^ @ *).word= > {
                    word.slice(0.3) + word.slice(3).replace(/.{1}/g.The '*')}}}return str
}

const encryptPhone = (str) = > regularEncrypt(str, 'phone')// Phone number desensitization
const encryptEmail = (str) = > regularEncrypt(str, 'email')// Desensitization of mailbox

// Rule object
const rules = {
    phone: encryptPhone,
    email: encryptEmail
}

exports.encryRules = {
    rules
}
Copy the code

The last

The above features are sufficient for general scenarios 🤪. For Winston’s other features, I will not describe them here. For details, please go to Github to check the relevant documentation.

Finally, I hope you can give me a thumbs-up, MUA ~