Koa日志中间件封装开发详解


Posted in Javascript onMarch 09, 2019

对于一个服务器应用来说,日志的记录是必不可少的,我们需要使用其记录项目程序每天都做了什么,什么时候发生过错误,发生过什么错误等等,便于日后回顾、实时掌握服务器的运行状态,还原问题场景。

日志的作用

  • 记录服务器程序运行状态;
  • 帮助开发者快速捕获错误,定位以及决解故障。

日志中间件开发工具log4js

  1. 在node当中没有自带的日志模块,所以需要使用第三方模块
  2. 使用模块:log4js
  3. 安装: npm i log4js -S
  4. logsjs官方文档
  5. 日志分类:
    1. 访问日志: 记录客户端对项目的访问,主要是 http 请求。用于帮助改进和提升网站的性能和用户体验;
    2. 应用日志: 项目标记和记录位置打印的日志,包括出现异常情况,方便查询项目的运行状态和定位bug(包含了debug、info、warn 和 error等级别)。

日志等级

  • 如果配置了日志等级,则其只能记录日志等级比设置的更高级别的日志信息
  • 日志等级图

Koa日志中间件封装开发详解

如配置level: 'error',则只能输出error,fatar,mark级别的日志信息

日志中间件开发

设置需要日志需要记录的信息段(log_info.js)

export default (ctx, message, commonInfo) => {
  const {
   method, // 请求方法
   url,     // 请求链接
   host,   // 发送请求的客户端的host
   headers   // 请求中的headers
  } = ctx.request;
  const client = {
   method,
   url,
   host,
   message,
   referer: headers['referer'], // 请求的源地址
   userAgent: headers['user-agent'] // 客户端信息 设备及浏览器信息
  }
  return JSON.stringify(Object.assign(commonInfo, client));
}

设置通用获取配置后的log4js对象(logger.js)

const getLog = ({env, appLogLevel, dir}, name) => {
  
  //log4js基本说明配置项,可自定义设置键名,用于categories.appenders自定义选取
  let appenders = {
    // 自定义配置项1
    cheese: {
      type: 'dateFile', //输出日志类型
      filename: `${dir}/task`, //输出日志路径
      pattern: '-yyyy-MM-dd.log', //日志文件后缀名(task-2019-03-08.log)
      alwaysIncludePattern: true
    }
  }
  // 如果为开发环境配置在控制台上打印信息
  if (env === "dev" || env === "local" || env === "development") {
    // 自定义配置项2
    appenders.out = {
     type: "stdout"
    }
  }
  // log4js配置
  let config = {
    appenders,
    //作为getLogger方法获取log对象的键名,default为默认使用
    categories: {
     default: {
      appenders: Object.keys(appenders), // 取appenders中的说有配置项
      level: appLogLevel
     }
    }
  }
  log4js.configure(config) //使用配置项
  return log4js.getLogger(name)// 这个cheese参数值先会在categories中找,找不到就会默认使用default对应的appenders,信息会输出到yyyyMMdd-out.log
}

log日志中间件开发(logger.js)

export default (options) => {
  const contextLogger = {}; //后期赋值给ctx.log
  const { env, appLogLevel, dir, serverIp, projectName } = Object.assign({}, baseInfo, options || {});
  // 取出通用配置(项目名,服务器请求IP)
  const commonInfo = { projectName, serverIp };

  const logger = getLog({env, appLogLevel, dir},'cheese');

  return async (ctx, next) => {
    const start = Date.now(); //日志记录开始时间
    // 将日志类型赋值ctx.log,后期中间件特殊位置需要记录日志,可直接使用ctx.log.error(err)记录不同类型日志
    methods.forEach((method, i) => {
      contextLogger[method] = (message) => {
        logger[method](logInfo(ctx, message, commonInfo))
      }
    })
    ctx.log = contextLogger;
    // 执行中间件
    await next()
    // 结束时间
    const responseTime = Date.now() - start;
    // 将执行时间记录logger.info
    logger.info(logInfo(ctx,
      {
        responseTime: `响应时间为${responseTime/1000}s`
      }, commonInfo)
    )
  }
}

中间件使用(app.js)

import Log from '../log/logger';
...
app.use(Log({
    env: app.env, // koa 提供的环境变量
    projectName: 'back-API',
    appLogLevel: 'debug',
    dir: 'logs',
    serverIp: ip.address()
  }))

其他特殊位置需要日志记录使用

ctx.log.error(err.stack); //记录错误日志
ctx.log.info(err.stack); // 记录信息日志
ctx.log.warn(err.stack); // 记录警告日志
...

运行截图

Koa日志中间件封装开发详解

log4js使用基本配置和流程解析

设置配置项,

// 配置项形式
{
  appenders:{
    [自定义key]:{}
  },
  categories:{
  }
}
// 配置
config: {
  appenders:{
    // 每一个属性可以看作为一个配置模块
    out: {
      type: 'dateFile', //输出日志类型
      filename: `log/task`, //输出日志路径
      pattern: '-yyyy-MM-dd.log', //日志文件后缀名(task-2019-03-08.log)
      ...//具体配置看官网
    },
    error: {
      type: 'dateFile',
      filename: 'log/error',
      pattern: '-yyyy-MM-dd.log'',
      "alwaysIncludePattern": true
    },
    stdout: { type: 'stdout' }, //在控制台上打印信息
  },
  // 通过categories来取出给log4js按需配置,返回配置后的log4js对象,每个属性配置相当于一个不同的log4js配置对象入口;default为默认入口(getLogger()找不到入口时默认使用default)
  categories:{
    // 配置默认入口,使用appenders中的'stdout','out'配置模块,记录trace以上等级日志
    default: { appenders: ['stdout','out'], level: 'trace' },
    // 配置error门入口,使用appenders中的'stdout','err'配置模块,记录error以上等级日志
    error : {appenders: ['err'], level: 'error'}
  }
}

使用let logger_out = log4js.getLogger('app');

log4js.getLogger('app')查找特定log4js对象流程:先根据app参数值在categories中找,发现没有app,然后就会默认使用default对应的appenders进行配置,即信息会输出到log/task-yyyy-mm-dd.log文件中,并且会输出到控制台

使用let logger_out = log4js.getLogger('error');

根据error参数值在categories中找,发现没有拥有error配置,然后就会使用error对应的appenders进行配置,即信息会输出到log/error-yyyy-mm-dd.log文件中,因为error的配置项appenders中没有使用stdout模块,所以信息不会输出到控制台

后期考虑

是否需要对日志进行数据库存储,进行日志持久化;

考虑到不可能对日志记录后一直保存,对于一个月或者一周以前的日志可能没有必要在进行存储了,需要开发设置定时自动删除过期日志文件(获数据库日志记录)

Javascript 相关文章推荐
深入理解JavaScript系列(29):设计模式之装饰者模式详解
Mar 03 Javascript
《JavaScript函数式编程》读后感
Aug 07 Javascript
javascript生成img标签的3种实现方法(对象、方法、html)
Dec 25 Javascript
js将json格式的对象拼接成复杂的url参数方法
May 25 Javascript
Vue实现typeahead组件功能(非常靠谱)
Aug 26 Javascript
JS设计模式之观察者模式实现实时改变页面中金额数的方法
Feb 05 Javascript
小程序ios音频播放没声音问题的解决
Jul 11 Javascript
微信小程序购物车、父子组件传值及calc的注意事项总结
Nov 14 Javascript
解决JS表单验证只有第一个IF起作用的问题
Dec 04 Javascript
vue slot与传参实例代码讲解
Apr 28 Javascript
Vue中用JSON实现刷新界面不影响倒计时
Oct 26 Javascript
Vue h函数的使用详解
Feb 18 Vue.js
详解vue2.6插槽更新v-slot用法总结
Mar 09 #Javascript
Node.js Stream ondata触发时机与顺序的探索
Mar 08 #Javascript
详解JSON和JSONP劫持以及解决方法
Mar 08 #Javascript
Node.js Event Loop各阶段讲解
Mar 08 #Javascript
vue基础之data存储数据及v-for循环用法示例
Mar 08 #Javascript
vue.js使用v-model实现表单元素(input) 双向数据绑定功能示例
Mar 08 #Javascript
JavaScript解析机制与闭包原理实例详解
Mar 08 #Javascript
You might like
连接到txt文本的超链接,不直接打开而是点击后下载的处理方法
2009/07/01 PHP
php实现的Timer页面运行时间监测类
2014/09/24 PHP
php 使用redis锁限制并发访问类示例
2016/11/02 PHP
PHP实现二维数组中的查找算法小结
2018/06/09 PHP
jquery 结合C#后台的数组对文章的关键字自动添加链接的代码
2011/07/15 Javascript
js遍历、动态的添加数据的小例子
2013/06/22 Javascript
中止javascript执行的方法
2014/02/14 Javascript
jQuery学习笔记之基础中的基础
2015/01/19 Javascript
浅谈关于JavaScript API设计的一些建议和准则
2015/06/24 Javascript
基于JavaScript短信验证码如何实现
2016/01/24 Javascript
微信小程序 参数传递实例代码
2017/03/20 Javascript
jQuery的时间datetime控件在AngularJs中的使用实例(分享)
2017/08/17 jQuery
vue2单元测试环境搭建
2018/05/24 Javascript
Node.js折腾记一:读指定文件夹,输出该文件夹的文件树详解
2019/04/20 Javascript
angular使用md5,CryptoJS des加密的方法
2019/06/03 Javascript
浅谈vue项目用到的mock数据接口的两种方式
2019/10/09 Javascript
微信小程序实现canvas分享朋友圈海报
2020/06/21 Javascript
[32:39]完美世界DOTA2联赛循环赛 Forest vs Inki BO2第一场 11.04
2020/11/04 DOTA
Django组件content-type使用方法详解
2019/07/19 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
2019/08/05 Python
在Python中使用filter去除列表中值为假及空字符串的例子
2019/11/18 Python
解析python 类方法、对象方法、静态方法
2020/08/15 Python
在css3中background-clip属性与background-origin属性的用法介绍
2012/11/13 HTML / CSS
来自圣地亚哥的实惠太阳镜:Knockaround
2018/08/27 全球购物
英国在线泳装店:Simply Swim
2019/05/05 全球购物
雪山饭庄的创业计划书范文
2014/01/18 职场文书
2014年社区重阳节活动策划方案
2014/09/16 职场文书
教师三严三实学习心得体会
2014/10/11 职场文书
优秀员工自荐书
2015/03/06 职场文书
2015个人简历自我评价语
2015/03/11 职场文书
如何书写授权委托书?
2019/06/25 职场文书
导游词之南昌滕王阁
2019/11/29 职场文书
SQLServer 日期函数大全(小结)
2021/04/08 SQL Server
POST提交数据常见的四种方式
2022/01/18 HTML / CSS
Java9新特性之Module模块化编程示例演绎
2022/03/16 Java/Android
Python安装及建立虚拟环境的完整步骤
2022/06/25 Servers