Appium源码研究(10)-logger模块

// set up distributed logging before everything else
var npmlog = global._global_npmlog = require('npmlog');
// npmlog is used only for emitting, we use winston for output
//不利用npmlog输出信息,而是利用winston来输出,所以将npmlog的等级设置为silent
npmlog.level = "silent";
//日志管理系统
var winston = require('winston')
//文件操作模块
  , fs = require('fs')
  //获取系统信息的模块
  , os = require('os')
  //处理和转换文件路径
  , path = require('path')
  //工具模块
  , util = require('util');
  //日期处理模块
require('date-utils');

//log等级定义
var levels = {
  debug: 1
, info: 2
, warn: 3
, error: 4
};
//各等级对应的字体颜色
var colors = {
  info: 'cyan'
, debug: 'grey'
, warn: 'yellow'
, error: 'red'
};
//定义log等级对应关系的字典
var npmToWinstonLevels = {
  silly: 'debug'
, verbose: 'debug'
, info: 'info'
, http: 'info'
, warn: 'warn'
, error: 'error'
};

var logger = null;
//时区
var timeZone = null;
//堆栈
var stackTrace = null;

// capture any logs emitted by other packages using our global distributed
// logger and pass them through winston
npmlog.on('log', function (logObj) {
  //根据传入的参数,得到对应的log等级,如果参数未被定义过,也就是说在字典中未找到,那么就设置为info
  var winstonLevel = npmToWinstonLevels[logObj.level] || 'info';
  //获得消息体
  var msg = logObj.message && logObj.prefix ?
              (logObj.prefix + ": " + logObj.message) :
              (logObj.prefix || logObj.message);
  logger[winstonLevel](msg);
  console.log("==========npmlog.on=================");
});
//将当前日期转化为YYYY-MM-DD HH24:MI:SS:LL形式
var timestamp = function () {
  var date = new Date();
  if (!timeZone) {
    //date.getTimezoneOffset()时差,精确到分钟,乘以60000,精确到毫秒数
    date = new Date(date.valueOf() + date.getTimezoneOffset() * 60000);
  }
  return date.toFormat("YYYY-MM-DD HH24:MI:SS:LL");
};

// Strip the color marking within messages.
// We need to patch the transports, because the stripColor functionality in
// Winston is wrongly implemented at the logger level, and we want to avoid
// having to create 2 loggers.
//解决winston在日志实现上bug,有时候会产生2个logger器
function applyStripColorPatch(transport) {
  var _log = transport.log.bind(transport);
  transport.log = function (level, msg, meta, callback) {
    var code = /\u001b\[(\d+(;\d+)*)?m/g;
    msg = ('' + msg).replace(code, '');
    _log(level, msg, meta, callback);
  };
}
//将log信息传输到控制台上
var _createConsoleTransport = function (args, logLvl) {
  var transport = new (winston.transports.Console)({
    name: "console"
    , timestamp: args.logTimestamp ? timestamp : undefined
    , colorize: !args.logNoColors
    , handleExceptions: true
    , exitOnError: false
    , json: false
    , level: logLvl
  });
  if (args.logNoColors) applyStripColorPatch(transport);
  return transport;
};
//将log信息传输到文件中
var _createFileTransport = function (args, logLvl) {
  var transport = new (winston.transports.File)({
      name: "file"
      , timestamp: timestamp
      , filename: args.log
      , maxFiles: 1
      , handleExceptions: true
      , exitOnError: false
      , json: false
      , level: logLvl
    }
  );
  applyStripColorPatch(transport);
  return transport;
};
//将log信息传输到webhook中,webhook就是将log信息传输到某一个指定url上,将log信息传输到web服务器上
var _createWebhookTransport = function (args, logLvl) {
  var host = null,
      port = null;

  if (args.webhook.match(':')) {
    var hostAndPort = args.webhook.split(':');
    host = hostAndPort[0];
    port = parseInt(hostAndPort[1], 10);
  }

  var transport = new (winston.transports.Webhook)({
    name: "webhook"
    , host: host || '127.0.0.1'
    , port: port || 9003
    , path: '/'
    , handleExceptions: true
    , exitOnError: false
    , json: false
    , level: logLvl
  });
  applyStripColorPatch(transport);
  return transport;
};
//管理分配上面三种log表现形式:控制台,文件,web。其中控制台是必须的,文件和web是根据配置来产生的
var _createTransports = function (args) {
  var transports = [];
  var consoleLogLevel = null,
      fileLogLevel = null;

  if (args.loglevel && args.loglevel.match(":")) {
    // --log-level arg can optionally provide diff logging levels for console and file  separated by a colon
    var lvlPair = args.loglevel.split(':');
    consoleLogLevel =  lvlPair[0] || consoleLogLevel;
    fileLogLevel = lvlPair[1] || fileLogLevel;
  } else {
    consoleLogLevel = fileLogLevel = args.loglevel;
  }

  transports.push(_createConsoleTransport(args, consoleLogLevel));

  if (args.log) {
    try {
      // if we don't delete the log file, winston will always append and it will grow infinitely large;
      // winston allows for limiting log file size, but as of 9.2.14 there's a serious bug when using
      // maxFiles and maxSize together. https://github.com/flatiron/winston/issues/397
      if (fs.existsSync(args.log)) {
        fs.unlinkSync(args.log);
      }

      transports.push(_createFileTransport(args, fileLogLevel));
    } catch (e) {
      console.log("Tried to attach logging to file " + args.log +
                  " but an error occurred: " + e.msg);
    }
  }

  if (args.webhook) {
    try {
      transports.push(_createWebhookTransport(args, fileLogLevel));
    } catch (e) {
      console.log("Tried to attach logging to webhook at " + args.webhook +
                  " but an error occurred. " + e.msg);
    }
  }

  return transports;
};

var _appDir = path.dirname(require.main.filename);
//将堆栈信息转化为字符串
var _stackToString = function (stack) {
  var str = os.EOL + "    [------TRACE------]" + os.EOL;
  var len = stack.length < 15 ? stack.length : 15;

  for (var i = 0; i < len; i++) {
      var fileName = stack[i].getFileName();
      // ignore calls from this file
      if (fileName === __filename) continue;
      var substr = "    at ";
    try {
      var typeName = stack[i].getTypeName();

      substr += util.format("%s.%s (%s:%d:%d)" + os.EOL, typeName, stack[i].getFunctionName(),
                  path.relative(_appDir, stack[i].getFileName()), stack[i].getLineNumber(),
                  stack[i].getColumnNumber());
      str += substr;

    } catch (e) { }
  }

  return str;
};

var _addStackTrace = function (fn, stackTrace) {
  var _fn = fn;
  return function (msg) {
    //os.EOL为行结束符
    _fn(msg + os.EOL + _stackToString(stackTrace.get()) + os.EOL);
    console.log("==========_addStackTrace=================");
  };
};
//log系统初始化
module.exports.init = function (args) {
  // set de facto param passed to timestamp function
  timeZone = args.localTimezone;

  // by not adding colors here and not setting 'colorize' in transports
  // when logNoColors === true, console output is fully stripped of color.
  if (!args.logNoColors) {
    winston.addColors(colors);
  }
  //初始化log器,准备控制台输出(文件和webhook都准备好)
  logger = new (winston.Logger)({
    transports: _createTransports(args)
  });
  //设置log等级
  logger.setLevels(levels);

  // 8/19/14 this is a hack to force Winston to print debug messages to stdout rather than stderr.
  // TODO: remove this if winston provides an API for directing streams.
  //将debug的信息转化为info等级的lgo
  if (levels[logger.transports.console.level] === levels.debug) {
    logger.debug = function (msg) { logger.info('[TesterHome] ' + msg); };
  }
  //如果允许同步堆栈信息,就要在相应的log输出信息中追加这些信息
  if (args.asyncTrace) {
    stackTrace = require('stack-trace');
    //将堆栈信息追加到等级为info的log信息中,后面两个类似
    logger.info = _addStackTrace(logger.info, stackTrace);
    logger.warn = _addStackTrace(logger.warn, stackTrace);
    logger.error = _addStackTrace(logger.error, stackTrace);
  }
};
//以get函数对外提供
module.exports.get = function () {
  if (logger === null) {
    exports.init({});
  }
  return logger;
};

你可能感兴趣的:(框架学习[Appium],测试工程师成长之路)