nodejs-日志收集

开发过程中,日志记录是必不可少的事情,尤其是生产系统中经常无法调试,因此日志就成了重要的调试信息来源。

1.expressWinston

访问日志一般用来记录每个客户端对应用的访问请求。在Web应用中,主要记录HTTP请求中的关键数据。如下所示记录:

{
    "res":{
        "statusCode":200
    },
    "req":{
        "url":"/user/login",
        "headers":{
            "host":"localhost:8001",
            "connection":"keep-alive",
            "content-length":"348",
            "postman-token":"0ef999d7-1b51-4bc5-c4d8-f067f165b2be",
            "cache-control":"no-cache",
            "origin":"chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop",
            "user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
            "content-type":"multipart/form-data; boundary=----WebKitFormBoundaryef7X45UMFFjdZwsB",
            "accept":"*/*",
            "dnt":"1",
            "accept-encoding":"gzip, deflate, br",
            "accept-language":"zh-CN,zh;q=0.8,en;q=0.6",
            "cookie":"SID=s%3AzPVGIcZLGk9osGdrAOpF9BULNZJnGwkj.INJURLoEcBKNFZlvCNonwpqFJq56lXlpQFIu15c6N1Y"
        },
        "method":"POST",
        "httpVersion":"1.1",
        "originalUrl":"/user/login",
        "query":{

        }
    },
    "responseTime":39,
    "level":"info",
    "message":"HTTP POST /user/login",
    "timestamp":"2017-09-12T08:25:44.568Z"
}

中间件winston express-winston提供了请求的完整记录,记录的内容有:

  • res结果
  • req请求包含:url, headlers, method, httpVersion, originalUrl, query六大项
  • responseTime相应时间
  • level当前日志等级
  • message 请求基本信息
  • timestamp 时间戳

其中对于日志中是否包含哪些信息,中间件也做了比较好的支持:

expressWinston.requestWhitelist.push('body');
expressWinston.responseWhitelist.push('body');

参考:express-winston

记录日志的目的是为了分析,当前的这些数据已经足够用来帮助分析Web应用的用户分布情况、服务器端的相应时间、相应状态和客户端类型等。通过这些数据反过来帮助我们改进和提升网站。在nodejs中可以使用express-winston将日志打印在控制台,也可以将其打印在本地文件中,还可以将其写入至数据库中。写法如下:

import winston from 'winston';
import expressWinston from 'express-winston';
import winstonMongodb from 'winston-mongodb';

const MongoDB = winstonMongodb.MongoDB;

expressWinston.requestWhitelist.push('body');
app.use(expressWinston.logger({
    transports: [
        // 控制台
        new winston.transports.Console({
            json: true,
            colorize: true
        }),
        // 文件
        new winston.transports.File({
          filename: './logs/success.log'
        }),
        // mongodb数据库
        new winston.transports.MongoDB({ 
            db: config.logsUrl
        })
    ],
    meta: true,
    msg: "HTTP {{req.method}} {{req.url}}",
    expressFormat: true,
    colorize: true,
    ignoreRoute: function (req, res) { return false; }
}));

其中db的写法参考如下,常见问题

db : 'mongodb://localhost:27017/Book-catalog',

有的开发者可能不太了解,会选择将一些日志写入到数据库中。数据库比日志好的地方在于它是结构化的数据,可以直接使用SQL语言进行查询和统计,日志文件则需要在加工之后才能分析。
但是日志文件和数据库在写入性能上是两个级别,数据库在写入过程中要经历一系列处理,比如锁表、日志等操作。写日志文件则是直接将数据写到磁盘上。相比之下,写日志是轻量型操作,将日志分析和日志记录步骤分离开来是较好的选择。

线上业务可能访问量巨大,产生的日志也可能是大量的,上述示例只是简单的将普通日志和异常日志分开存放至两个文件中,日志过多时也不便产看。为此,将产生的日志按日期分割是必要的。expressWinston可以使用winston-daily-rotate-file,引入第三个库,目前看有点略麻烦,同时还不能将自己的输出打入到日志中,我们准备介绍下一个日志工具log4js

2.log4js

Node.js,已经有现成的开源日志模块,就是log4js,源码地址:点击打开链接
项目引用方法:

npm install log4js

用法也是非常见简单:

var log4js = require('log4js');
var logger = log4js.getLogger();
logger.level = 'debug'; // default level is OFF - which means no logs at all.
logger.debug("Some debug messages");

配置

log4js.configure(object || string)

配置属性包含有:

  • levels:用来定义日志等级
  • appenders:map结构,定义日志输入,必需定义type字段
  • categories:map结构,定义分类,必需定义default分类,分离里面包含appenders和level属性
  • pm2:pm2日志

实例1

const log4js = require('log4js');
log4js.configure({
  appenders: {
    out: { type: 'stdout' },
    app: { type: 'file', filename: 'application.log' }
  },
  categories: {
    default: { appenders: [ 'out', 'app' ], level: 'debug' }
  }
});

代码中定义了两个appenders,分别为out和app。out使用stdout追加日志方法,独立输出。app使用文件作为输入,将内容输出到application.log

Loggers

log4js.getLogger([category])

输入哪种分类的log

Shutdown

log4js.shutdown(cb)

shutdown方法用于关闭log的接收,结束所有写日志事件。

Custom Layouts

log4js.addLayout(type, fn)

用户自定义数据记录格式。

实例2

log4js.configure({
  appenders: { 'out': { type: 'stdout', layout: { type: 'basic' } } },
  categories: { default: { appenders: ['out'], level: 'info' } }
});
const logger = log4js.getLogger('cheese');
logger.error('Cheese is too ripe!');

该配置替换stdout的默认格式coloured,替换为basic。输入为:

[2017-03-30 07:57:00.113] [ERROR] cheese - Cheese is too ripe!

实例3

log4js.configure({
  appenders: { out: { type: 'stdout', layout: { type: 'messagePassThrough' } } },
  categories: { default: { appenders: [ 'out' ], level: 'info' } }
});
const logger = log4js.getLogger('cheese');
const cheeseName = 'gouda';
logger.error('Cheese is too ripe! Cheese was: ', cheeseName);

该配置替换stdout的默认格式coloured,替换为messagePassThrough。输入为:

Cheese is too ripe! Cheese was: gouda

实例4
不得不说的type: 'pattern',通过正则输出相应的格式化数据:

log4js.configure({
  appenders: {
    out: { type: 'stdout', layout: {
      type: 'pattern',
      pattern: '%d %p %c %X{user} %m%n'
    }}
  },
  categories: { default: { appenders: ['out'], level: 'info' } }
});
const logger = log4js.getLogger();
logger.addContext('user', 'charlie');
logger.info('doing something.');

通过正则匹配也可以定义传递变量,如上代码的user。输出为:

2017-06-01 08:32:56.283 INFO default charlie doing something.

实例5
定义自己的输出格式,type: 'json',如下所示:

const log4js = require('log4js');

log4js.addLayout('json', function(config) {
  return function(logEvent) { return JSON.stringify(logEvent) + config.separator; }
});

log4js.configure({
  appenders: {
    out: { type: 'stdout', layout: { type: 'json', separator: ',' } }
  },
  categories: {
    default: { appenders: ['out'], level: 'info' }
  }
});

const logger = log4js.getLogger('json-test');
logger.info('this is just a test');
logger.error('of a custom appender');
logger.warn('that outputs json');
log4js.shutdown(() => {});

输出为:

{"startTime":"2017-06-05T22:23:08.479Z","categoryName":"json-test","data":["this is just a test"],"level":{"level":20000,"levelStr":"INFO"},"context":{}},
{"startTime":"2017-06-05T22:23:08.483Z","categoryName":"json-test","data":["of a custom appender"],"level":{"level":40000,"levelStr":"ERROR"},"context":{}},
{"startTime":"2017-06-05T22:23:08.483Z","categoryName":"json-test","data":["that outputs json"],"level":{"level":30000,"levelStr":"WARN"},"context":{}},

具体使用

新建log.js文件,内容如下所示:

var log4js = require('log4js');
log4js.configure({
    appenders: {
        out: { type: 'console' }, //控制台输出  
        app: {
            type: "dateFile",
            filename: 'logs/log.log',
            pattern: "_yyyy-MM-dd",
            alwaysIncludePattern: false,
        }//日期文件格式  
    },
    categories: {
        default: { appenders: ['out'], level: 'info' },
        logFile: { appenders: ['out', 'app'], level: 'ALL' },
    },
    replaceConsole: true,   //替换console.log  
});

var fileLog = log4js.getLogger('logFile');
exports.logger = fileLog;
exports.use = function (app) {
    //页面请求日志
    app.use(log4js.connectLogger(fileLog));
}

输入的log如下所示:

[2017-09-18 20:37:02.366] [INFO] logFile - ::1 - - "POST /user/login HTTP/1.1" 200 393 "" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • from:https://www.cnblogs.com/ITtangtang/p/3926665.html一、L...
    enshunyan阅读 3,286评论 0 0
  • (http://www.cnblogs.com/zhangchenliang/p/4546352.html) 1、...
    凌雲木阅读 2,417评论 0 2
  • 在应用程序中添加日志记录总的来说基于三个目的:监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析...
    时待吾阅读 4,979评论 0 6
  • 浅谈Logger日志输出不同的日志文件 其实一直想动态生成路径和文件名称,但是log4j只能加载系统环境变量类似于...
    幽澜先生阅读 8,080评论 0 0
  • 转眼,2019只剩最后一天了,回头望望,唏嘘嗟叹,这一年经历了太多,总算平安度过。 一月一日元旦,以往工作生涯里必...
    秋秋历史苑阅读 91评论 0 1