专栏首页前端杂货铺高吞吐koa日志中间件

高吞吐koa日志中间件

Midlog中间件

node服务端开发中少不了日志打点,而在koa框架下的日志打点在多进程环境中日志信息往往无法对应上下文,而且在高并发下直接进行写buffer操作(内核调用writev)也会造成内存泄漏,因此Midlog就是为了缓解这种问题而产生的,其采用多种缓冲调度策略尽可能降低writev的代价,减缓内存溢出的速率。

日志系统的三个关键要素:稳定、性能和易用。


当前log4js遇到的问题。


设计新系统的前提。


回顾node原生模块--“流”的读与写,涉及到了多个缓冲区以及他们缓冲区的实现(stream2.0的 Transform流同时包括了读写缓冲区)


优化点与优化方式。


Midlog系统架构图,其中reqContainer收集同一个上下文的日志信息并转码;

StrategyMaker负责制定双缓冲缓存策略和超时刷新以及强制刷新策略;

Writeable则负责真正的写buffer操作,内部通过链表保存数据;


详细介绍每个模块的功能。


快速使用

app.js

var koa = require('koa');
var midlog = require('midlog');
var app = koa();

// 配置日志中间件
var firstValve = midlog({
  env: 'online',
  exportGlobalLogger: true,
  appender: [{
    type: 'INFO',
    logdir: '/tmp/log/midlog',
    pattern: '%d %r %x{name}:%z %p - %m%n',
    rollingFile: false,
    duation: 60000,
    name: 'info.log',
    nameformat: '[info.]HH-mm-ss[.log]',
    tokens: {
      name: 'helloworld'
    },
    cacheSize: 5 * 1024 * 1024,
    flushTimeout: 15000
  },{
  type: 'ERROR',
    logdir: '/tmp/log/midlog',
    pattern: '%d %r %x{name}:%z %p - %m%n',
    rollingFile: false,
    duation: 60000,
    name: 'error.log',
    nameformat: '[info.]HH-mm-ss[.log]',
    tokens: {
      name: 'helloworld'
    },
    cacheSize: 10240,
    flushTimeout: 10000
},{
    type: 'TRACE',
    logdir: '/tmp/log/midlog',
    pattern: '%d %r %x{name}:%z %p - %m%n',
    rollingFile: false,
    duation: 60000,
    name: 'trace.log',
    nameformat: '[info.]HH-mm-ss[.log]',
    tokens: {
      name: 'helloworld'
    },
    cacheSize: 5 * 1024 * 1024,
    flushTimeout: 10000
  }]
});

// 使用全局的logger接口
logger.info('i am the global logger');

// 将midlog放在中间件的前列
app.use(firstValve);

// 业务中间件
app.use(function*(next){
  this.logger.info(this.url+' this is the first valve!! ');
  this.logger.error('midlog tracing' + this.url+' this is the first valve!! ');
  this.logger.trace('midlog tracing' + this.url+' this is the first valve!! ');
  yield next;
});

app.use(function*(){
  this.logger.info(this.url+' this is the 2cd valve!! ');
  this.logger.error('midlog tracing' + this.url+' this is the 2cd valve!! ');
  this.logger.trace('midlog tracing' + this.url+' this is the 2cd valve!!');
  this.body = '<h1>hello midlog</h1>';
});

app.listen(8888);

功能

midlog提供了3种日志刷新级别:

TRACE、INFO、ERROR

并且提供了两种写日志文件的方式:

  • 单文件写 (通过设置appender的rollingFile为false触发)
  • 文件分时间片写 (通过设置appender的rollingFile为true触发)

midlog采用和log4js相同的layout格式和语法,生成可定制的日志输出格式。

最后,midlog采用多级缓冲的架构(针对单文件写模式采用双缓冲,文件分时写模式采用单缓冲),可以有效的控制Stream写的频率,而缓冲的大小和刷新频率可以由开发者根据实际需要自由设置。

配置

  • env {String} 环境设置。若设置为development,则会在控制台和文件中同时输出日志
  • exportGlobalLogger {Boolean} 是否保留全局logger对象。设置为true,则在全局使用logger对象
  • appender {Array} 日志类型配置数组。数组每一项描述每种类型日志的相关信息及缓冲刷新频率

appender详解

  • type {String} 日志类型。可以为 “INFO、TRACE和ERROR” 任意一种
  • logdir {String} 日志文件所在的绝对目录
  • rollingFile {Boolean} 是否按照时间进行日志文件分割。设置为true时则按照设置的duration间隔分割文件
  • duration {Number} 分割日志文件的间隔。若rollingFile为true,则按照duration大小分割文件
  • name {String} 日志文件名称。name属性在单文件写模式下有效,在rollingFile == true时无效
  • nameformat {String} 日志文件格式匹配定义。nameformat属性在文件分时间片写模式下有效,即rollingFile == true 格式定义的字符串意义如下所示: 'd': 日期和时间, 'h': 主机名称, 'm': 日志信息格式化,主要优化错误输出, 'n': 换行符, 'p': 日志级别, 'r': 时间输出, 'z': 进程号输出, '%': 百分号占位符, 'x': 用户自定义变量或函数,搭配{token}属性
  • tokens {Object} 与nameformat搭配使用,对象的属性值可为常亮,也可为函数

如定义nameformat为 pattern: '%d %r %x{name}:%z %p - %m%n' 且tokens设置为 {name: 'helloworld'}

则输出日志格式为:

(%d) (%r) (%x{name}) (%z) (%p) (%m) (%n)
2017-01-16 10:59:55.611 10:59:55 helloworld:13736 INFO - / this is the first valve!!
  • cacheSize {Number} 缓冲大小,单位字节。midlog在单文件写模式下采用双缓冲结构控制I/O速率,因此开发者可以通过定义缓冲大小实现高效的写入流程,默认为10kB大小;在文件分时间片写模式下该选项无效
  • flushTimeout {Number} 缓冲刷新间隔。在单文件写文件分时间片写两种模式下都起作用,定点刷新缓冲

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 简单拖拽实现

    拖拽的元素必须绝对定位。 在实际操作中,犯了一个简单的错误:   对于iframe元素的事件绑定,需要在src完全加载后进行绑定。 iframe.onload ...

    欲休
  • 简单的导航栏实现

    为了seo优化,导航栏最好标签语义化。 最符合语义的莫过于ul,li。在采用多级标签时,可采用控制二级标签ul位置的方式来进行显隐。 效果如下: http://...

    欲休
  • 富文本编辑器的一些要点

    重新复习了富文本编辑器的基本实现,现总结下要点:   1,iframe的创建,之所以使用iframe框架,是为了防止编辑的内容影响当前文档。   2,隐藏的te...

    欲休
  • vue.js响应式原理解析与实现—实现v-model与{{}}指令

    上一节我们已经分析了vue.js是通过Object.defineProperty以及发布订阅模式来进行数据劫持和监听,并且实现了一个简单的demo。今天,我们就...

    嘿嘿嘿
  • 「译」什么是抽象语法树

    AST 是抽象语法树的缩写词,表示编程语言的语句和表达式中生成的 token。有了 AST,解释器或编译器就可以生成机器码或者对一条指令求值。

    Chor
  • 推荐系统之用户行为分析

    用户行为介绍 基于用户行为的推荐,在学术界名为协同过滤算法。 协同过滤就是指用户可以齐心协力,通过不断地和网站互动,使 自己的推荐列表能够不断过滤掉自己不感...

    Spark学习技巧
  • (JavaScript) this的用法

    this.name为foo,所以this指向全局变量,即window。所以,一般将this作为变量保存下来。代码如下:

    前端博客 : alili.tech
  • Creator3D 打砖块子弹发射,以及摄像机平滑移动控制!

    前面一篇教程《Creator3D图文教程【打砖块】》,我们讲了打砖块游戏中的 3D 物体的场景布局、材质资源、物理刚体与碰撞组件,接下来本篇文章重点介绍“子弹的...

    张晓衡
  • KUOKUO的趣味教程 | 小怪物也思考(3)

    张晓衡
  • MongoDB日志文件过大清理

    MongoDB启动的时候指定了--logpath为一个日志文件。随着时间此文件会变得越来越大,达到好几个G。 因为不想让MongoDB进程重新启动,所以不能停...

    拓荒者

扫码关注云+社区

领取腾讯云代金券