当前位置: 首页 > 后端技术 > Node.js

Node 进阶:express 默认日志组件 morgan 从入门使用到源码剖析

时间:2023-04-03 23:54:00 Node.js

Node进阶:express默认日志组件morgan从入门到源码解析欢迎加入群交流,群号197339705。章节概述morgan是express默认的日志中间件,也可以独立作为node的日志组件使用。没有快递的js。本文由浅入深,内容主要包括:摩根入门示例如何将日志保存到本地文件如何将日志保存到本地文件编译入门示例首先,初始化项目。npminstallexpressmorgan然后,在basic.js中添加以下代码。varexpress=require('express');varapp=express();varmorgan=require('morgan');app.use(morgan('short'));app.use(函数(req,res,next){res.send('ok');});app.listen(3000);nodebasic.js运行程序,在浏览器中访问http://127.0.0.1:3000,打印日志如下?2016.12.11-advanced-morgangit:(master)?nodebasic.js::ffff:127.0.0.1-GET/HTTP/1.1304--3.019ms::ffff:127.0.0.1-GET/favicon.icoHTTP/1.12002-0.984ms打印日志到本地文件morgan支持stream配置项,可以用来实现日志到地面的效果,代码如下:varexpress=require('express');varapp=express();varmorgan=require('morgan');varfs=require('fs');varpath=require('path');varaccessLogStream=fs.createWriteStream(path.join(__dirname,'access.log'),{flags:'a'});app.use(morgan('short',{stream:accessLogStream}));app.use(function(req,res,next){res.send('确定');});app.listen(3000);用于讲解核心APImorgan的API很少,使用频率最高的是morgan(),它返回一个expresslog中间件。morgan(format,options)参数说明如下:format:可选,morgan定义了几种日志格式,每种格式都有对应的名称,如combined、short等,默认为default。可以在此处找到不同格式之间的差异。下面会说明,如果自定义日志格式。options:可选,配置项,包括stream(常用)、skip、immediate。stream:日志的输出流配置,默认为process.stdout。skip:是否跳过日志记录,如何使用可以参考这里。immediate:布尔值,默认为false。为true时,一收到请求就记录日志;如果为false,则在请求返回后记录日志。自定义日志格式,首先搞清楚morgan中的两个概念:format和token。很简单:format:日志格式,本质上是一个表示日志格式的字符串,比如:method:url:status:res[content-length]-:response-timems。token:format的一个组成部分,比如上面的:method,:url甚至是所谓的token。弄清楚format和token的区别后,可以看看morgan中关于自定义日志格式的关键API。摩根格式(名称,格式);//自定义日志格式morgan.token(name,fn);//自定义token自定义格式很简单,先通过morgan.format()定义名为joke的日志格式,然后通过morgan('joke')调用。varexpress=require('express');varapp=express();varmorgan=require('morgan');摩根。格式('笑话','[笑话]:方法:url:状态');应用程序。使用(摩根('笑话'));app.use(函数(req,res,next){res.send('ok');});app.listen(3000);看看运行结果?2016.12.11-advanced-morgangit:(master)?nodemorgan.format.js[joke]GET/304[joke]GET/favicon.ico200自定义token代码如下,自定义token通过morgan.token(),然后将自定义token添加到自定义格式中即可。varexpress=require('express');varapp=express();varmorgan=require('morgan');//自定义tokenmorgan.token('from',function(req,res){returnreq.query.from||'-';});//自定义格式,其中包含自定义tokenmorgan.format('joke','[joke]:method:url:status:from');//使用自定义格式app.use(morgan('笑话'));app.use(function(req,res,next){res.send('ok');});app.listen(3000);运行程序,在浏览器中访问http://127.0.0.1:3000/hello?f...和http://127.0.0.1:3000/hello?f...?2016.12.11-advanced-morgangit:(master)?nodemorgan.token.js[笑话]GET/hello?from=app200app[笑话]GET/favicon.ico304-[笑话]GET/hello?from=pc200pc[笑话]]GET/favicon.ico304-Advanced使用日志剪切在线应用程序。如果所有的日志都保存在同一个本地文件中,时间长了文件会变得很大,影响性能,而且不便于查看。这时候就需要日志切分了。借助file-stream-rotator插件,可以轻松完成日志拆分。除了file-stream-rotator相关的配置代码外,其余与前面的例子类似,这里不再赘述。varFileStreamRotator=require('file-stream-rotator')varexpress=require('express')varfs=require('fs')varmorgan=require('morgan')varpath=require('path')varapp=express()varlogDirectory=path.join(__dirname,'log')//确保日志目录存在fs.existsSync(logDirectory)||fs.mkdirSync(logDirectory)//创建一个旋转写入流varaccessLogStream=FileStreamRotator.getStream({date_format:'YYYYMMDD',filename:path.join(logDirectory,'access-%DATE%.log'),frequency:'daily',verbose:false})//设置loggerapp.use(morgan('combined',{stream:accessLogStream}))app.get('/',function(req,res){res.send('hello,world!')})将日志写入数据库有时候,我们会有这样的需求,将访问日志写入数据库。这种需求在需要实时查询统计的日志系统中很常见。如何在摩根实现这一目标?从文档来看,没有合适的扩展接口。于是查看了morgan的源码,发现实现起来非常简单。回顾之前的日志写入本地文件的例子,最关键的两行代码如下。通过stream指定日志的输出流。varaccessLogStream=fs.createWriteStream(path.join(__dirname,'access.log'),{flags:'a'});app.use(morgan('short',{stream:accessLogStream}));在morgan里面,大致的实现是这样的(经过简化)。//opt是配置文件varstream=opts.stream||process.stdout;varlogString=createLogString();//伪代码,根据format和token的定义,生成日志stream.write(logString);所以,你可以使用一种更棘手的方法来达到目的:声明一个带有write方法的对象,并将其作为流配置传入。varexpress=require('express');varapp=express();varmorgan=require('morgan');//具有写入方法的对象vardbStream={write:function(line){saveToDatabase(line);//伪代码,保存到数据库}};//使用dbStream作为流配置项的值app.use(morgan('short',{stream:dbStream}));app.use(function(req,res,next){res.send('ok');});app.listen(3000);深入分析morgan的代码,非常简洁。从设计上看,morgan的生命周期包括:token定义-->日志格式定义-->日志格式预编译-->请求到达/返回-->写入日志,其中已经提到了token定义和日志格式定义之前,这里只讲日志格式预编译的细节。和模板引擎预编译一样,日志格式预编译也是为了提高性能。源码如下,最关键的代码是compile(fmt)。functiongetFormatFunction(name){//查找格式varfmt=morgan[name]||名称||morgan.default//返回编译后的格式returntypeoffmt!=='function'?compile(fmt):fmt}compile()方法的实现细节这里不再赘述,重点关注compile(fmt)返回的内容:varmorgan=require('morgan');varformat=morgan['tiny'];varfn=morgan.compile(format);console.log(fn.toString());运行上面的程序,输出结果如下,其中token其实是morgan。functionanonymous(tokens,req,res/**/){return""+(tokens["method"](req,res,undefined)||"-")+""+(tokens["url"](req,res,undefined)||"-")+""+(tokens["status"](req,res,undefined)||"-")+""+(tokens["res"](req,res,"content-length")||"-")+"-"+(tokens["response-time"](req,res,undefined)||"-")+"ms";}见摩根.token()的定义很明确functiontoken(name,fn){morgan[name]=fnreturnthis}相关链接《Nodejs学习笔记》:https://github.com/chyingp/no...官方文档:https//github.com/expressjs/…