koa-logger源码解析

以GET申请“/”为例子,koa-logger会打印以下日志:

  <-- GET /GET / - 790ms  --> GET / 200 803ms 185b

分为是申请与响应的日志

// koa-logger 主函数function dev (opts) {  // print函数,默认参数为空时是调用console.log,如果有transporter选项,则调用transporter函数代替  const print = (function () {    let transporter    if (typeof opts === 'function') {      transporter = opts    } else if (opts && opts.transporter) {      transporter = opts.transporter    }    return function printFunc (...args) {      // 留神:这里做了格局说明符替换      const str = util.format(...args)      if (transporter) {        transporter(str, args)      } else {        console.log(...args)      }    }  }())  return async function logger (ctx, next) {    // request    const start = ctx[Symbol.for('request-received.startTime')] ? ctx[Symbol.for('request-received.startTime')].getTime() : Date.now()    // 打印申请日志    print('  ' + chalk.gray('<--') +      ' ' + chalk.bold('%s') +      ' ' + chalk.gray('%s'),    ctx.method,    ctx.originalUrl)    try {      await next()    } catch (err) {      // log uncaught downstream errors      log(print, ctx, start, null, err)      throw err    }    // calculate the length of a streaming response    // by intercepting the stream with a counter.    // only necessary if a content-length header is currently not set.    // 通过应用计数器拦挡流来计算响应流的长度。只有在以后没有设置Content-Length的响应报头时才须要。    const length = ctx.response.length    const body = ctx.body    let counter    if (length == null && body && body.readable) {      ctx.body = body        .pipe(counter = Counter())        .on('error', ctx.onerror)    }    // log when the response is finished or closed,    // whichever happens first.    // 记录响应实现或敞开的工夫,以先产生的工夫为准。个别都是触发finish事件    const res = ctx.res    const onfinish = done.bind(null, 'finish')    const onclose = done.bind(null, 'close')    // 重点:判断一个流申请是否完结    res.once('finish', onfinish)    res.once('close', onclose)    function done (event) {      res.removeListener('finish', onfinish)      res.removeListener('close', onclose)      // 打印 申请形式 门路 状态码 响应工夫 报文长度      log(print, ctx, start, counter ? counter.length : length, null, event)    }  }}

还有个logger辅助函数:

/** * Log helper. 封装的输入打点数据的辅助函数 */function log (print, ctx, start, len, err, event) {  // 从err获取响应的状态码  // 以下字段比方err.isBoom、err.output.statusCode、err.status等都是依据业务来的  // 个别业务都有对立的解决响应的中间件  // 留神500、404的解决辨别  const status = err    ? (err.isBoom ? err.output.statusCode : err.status || 500)    : (ctx.status || 404)  // set the color of the status code;  // 依据不同状态码区间,1xx、2xx、3xx、4xx、5xx,映射抉择不必色彩输入加以辨别  const s = status / 100 | 0  // eslint-disable-next-line  const color = colorCodes.hasOwnProperty(s) ? colorCodes[s] : colorCodes[0]  // get the human readable response length  let length  // 204、205、304非凡解决  if (~[204, 205, 304].indexOf(status)) {    length = ''  } else if (len == null) {    length = '-'  } else {    // 借助bytes库,计算响应内容大小、输入相似2b、2kb    length = bytes(len).toLowerCase()  }  const upstream = err ? chalk.red('xxx')    : event === 'close' ? chalk.yellow('-x-')      : chalk.gray('-->')  print('  ' + upstream +    ' ' + chalk.bold('%s') +    ' ' + chalk.gray('%s') +    ' ' + chalk[color]('%s') +    ' ' + chalk.gray('%s') +    ' ' + chalk.gray('%s'),  ctx.method,  ctx.originalUrl,  status,  time(start),  length)}/** * Show the response time in a human readable format. * In milliseconds if less than 10 seconds, * in seconds otherwise. */function time (start) {  const delta = Date.now() - start  return humanize(delta < 10000    ? delta + 'ms'    : Math.round(delta / 1000) + 's')}

总结

这个包代码很简略,不到200行代码,性能也很简略,须要拓展须要手动定制。