设计目标
- 统一日志工具,收拢console信息
- 开发过程,提供开发日志,方便调试和分析问题
- 根据不同环境,实现日志差异配置
- 提供染色、上报能力(规划中)
- 提供场景回溯能力(规划中)
日志规范
日志级别
- Debug:开发调试级别
- Info:记录系统的正常运行状态,方便追踪程序的位置和参数,辅助warning/error的问题查找
- Warning:表示系统可能出现问题,虽不用立即处理,但需要及时查看以排除风险
- Error:程序发生错误,影响用户使用,必须立即处理
日志格式
[module]:[YY-MM-DD HH:mm:ss:SSS]:[LEVEL] - message
-
module
模块名,建议使用当前文件名 -
YY-MM-DD HH:mm:ss.SSS
详细时间格式 -
LEVEL
日志级别 -
message
日志内容
// 示例
[app.js]:[17-10-30 15:35:22:123]:[info] - app started
常见场景
各模块独立创建logger实例
每个模块创建自己的实例,可以做日志隔离、筛选、自定义配置
代码:
// 假设当前模块名为exampleModule
const log = logger('exampleModule');
// 可以正常使用
log.debug('example debug');
也可以用logger.create工厂方法
// 假设当前模块名为exampleModule
const log = logger.create({
module: 'exampleModule',
// 个性化配置
// 定义日志标签,用于日志快速过滤
label: ['biz']
});
// 可以正常使用
log.debug('example debug');
输出:
[exampleModule]:[17-10-30 15:35:22:123]:[debug] - example debug
关键函数入参/出参
将关键函数的入参和出参打印出来,能快速定位函数是否正常运作,该类日志通常为info级别
代码:
function keyFn(arg1, arg2){
logger.info('keyFn args:', arg1, arg2);
// do something with args
logger.info('keyFn result:', ret);
return ret;
}
输出:
[exampleModule]:[17-10-30 15:35:22:123]:[info] - keyFn args: arg1 arg2
注:
- 多个参数在console中打印没有问题,但是上报时,需要做stringify
异步过程
异步过程容易出现异常,建议日志跟踪
代码:
// 三方调用一般为异步调用,增加唯一标识sn,方便与回调成对查看
const sn = logger.sn();
logger.info(sn, 'thunkFn args:', params);
thunkFn(params, function(err, res){
if(err){
logger.error(sn, 'thunkFn result:', err);
// handle error
return;
}
logger.info(sn, 'thunkFn result:', res);
});
输出:
[exampleModule]:[17-10-30 15:35:22:123]:[info] - [sn-123456] thunkFn args: params
...
[exampleModule]:[17-10-30 15:36:32:230]:[info] - [sn-123456] thunkFn result: res
注:
- promise/generator/async等异步场景,日志范式同上
三方调用(服务/bridge等)
对三方的接口调用,必须记录日志,该类日志通常为info级别
- 三方调用统一抽象为IO操作
- 目前三方调用唯一通道为bridge,会默认打印日志
- 日志范式同上异步过程
非可信输入/输出
- encodeURIComponent
- decodeURIComponent
- JSON.parse
- JSON.stringify
try {
let name = decodeURIComponent(params.name);
}
catch(err) {
logger.error(err);
name = '';
}
SDK强制日志规范
- 1.bridge调用
- 2.全局Error捕获
- 3.路由跳转信息,如 A->B->C->B->A, 方便复现和定位问题。
logger设计
全局接口和属性
logger.conf
全局配置
// logger.conf默认为logger.DEF_CONF.[process.ENV.NODE_ENV]
// 建议工程根据env,使用对应配置
logger.conf = {
/**
* 日志打印级别
* @enums {Number} logger.LEVEL
* @default logger.LEVEL.WARNING
*/
level: logger.LEVEL.DEBUG,
/**
* 日志输出位置,目前仅支持console
* @default logger.APPENDER.CONSOLE
*/
appender: logger.APPENDER.CONSOLE
};
logger(identifier[, options])
创建logger实例
const log = logger('moduleName');
// 日志打印时,自动带上模块名moduleName
// 可以通过logger.filter指定过滤条件
log.info('some msg');
const logWithConf = logger('moduleName', {
/**
* 定义日志标签,用于日志快速过滤
* @type {[String]}
*/
label: ['biz', 'someModuleType']
});
// 日志打印时,自动带上模块名moduleName
// 可以通过logger.filter指定过滤条件
log.info('some msg');
logger.create({ module, label, ...options })
@deprecated 创建logger实例,同logger(identifier[, options])
logger.filter
日志过滤器
// 默认为空,不做过滤
// 通过传入label数组,指定要查看的日志
// 此处为只看label为sdk的日志
logger.filter = ['sdk'];
// 通过filter函数指定日志
logger.filter = function(inst){
// 根据日志实例,判定是否显示
return true;
};
logger.sn()
工具方法,生成日志id,用于异步场景的日志追踪
const sn = logger.sn(); // [sn-123456]
模块log的接口和属性
log.conf
// log的conf默认继承logger.conf
log.conf = {
/**
* 定义日志标签,用于日志快速过滤
* @type {[String]}
*/
label: ['biz', 'someModuleType']
};
log.debug
log.debug(obj1, obj2, …)
log.debug({ a: 1 }, { b: 2 }, 'msg', true);
log.debug(msg, subst1, subst1, …)
log.debug('msg %d: %s', 1, 'msg content');
log.info
同log.debug
log.warning
同log.debug
log.error
同log.debug
日志实例
日志实例指,通过log的debug/info/warning/error产生的日志实例
const inst = log.debug('msg');
instance.module
模块名
instance.time
日志时间
instance.level
日志级别 debug/info/warning/error
instance.message
日志内容 @todo
常量
logger.LEVEL
日志级别常量定义
- logger.LEVEL.debug
- logger.LEVEL.info
- logger.LEVEL.warning
- logger.LEVEL.error
logger.DEF_CONF
logger.DEF_CONF.DEV
{
level: logger.LEVEL.DEBUG,
appender: logger.APPENDER.CONSOLE
}
logger.DEF_CONF.TEST
{
level: logger.LEVEL.INFO,
appender: logger.APPENDER.CONSOLE
}
logger.DEF_CONF.RELEASE
{
level: logger.LEVEL.ERROR,
appender: logger.APPENDER.CONSOLE
}
logger.APPENDER
-
logger.APPENDER.CONSOLE
使用console作为输出位置