如何让Node.js正确地日志

JavaScript016

如何让Node.js正确地日志,第1张

为了完成这点,你会需要使用一个最流行的开发包,叫做 winston. Winston 是一个 Node.js 的多通道异步传输日志库。

你可以通过安装 winston 的方式,添加它到你的项目中:

一旦你安装之后,你可以这样添加 winston 到你的项目:

上面的代码片段会推送接下来的这行信息到 stdout :

如同你所看到的那样,我们传递了 info 字符串到 winston ,从而告诉 winston 这个将要日志的事件有一个日志级别与之关联,也就是 info。默认情况下, winston 附带了接下来的几种日志级别 (也可以添加自定义的级别) :

你可以通过接下来的一行设置日志的级别:

在 RisingStack ,我们通常利用一个叫 LOG_LEVEL 的环境变量来配置。这样的话,我们就可以动态改变哪些内容应该被日志记录:

当你的日志级别是 silly 时,你应该尽可能的多记录所有的日志。尽管如此,你还是需要遵守一个经验法则:绝对不要记录凭证,密码以及任何敏感的信息。

“绝对不要记录凭证,密码以及任何敏感的信息。” via @RisingStack #nodejs #logging #security

点击直达 TWEET

通常来说,会有至少一个以上的用户访问日志,所以,日志事件里面包含凭证信息会增加额外的风险。

当你 开始使用微服务 , 从日志角度来看,最大的挑战就是分布式追踪。

在一个分布式的架构中,错误检测会令人沮丧,因为由于系统的瑕疵会导致大量的警报信息涌现。分布式追踪的目的就是为了消除这个问题,它可以提供更多的事务和错误的视角,而普通的日志文件并没有能力做到这些。

为了做到这点,你不得不使用一个所谓的相关标识符——并且把它传递给所有的参与事务操作的服务。然后,如果每行日志都标注了这个 ID ,你就可以搜索这些事件了。

为了生成一个唯一的 ID ,你可以这样使用 uuid 模块:

这个 ID 不得不在函数调用中传递,并且它也不得不被发送到下游的服务。 如同你可以想象到的,这不是个特别值当的任务。

这里讲到追踪,我们的 Node.js / 微服务监控 解决方案如下图所示。追踪相关的 ID 到请求的容器,并且可视化了一个简单的树状图的数据流。你不会需要去搜索日志,因为它会直接以一种容易理解的方式去展示你的事务:

Node.js distributed tracing with trace by risingstack

根据这种方式, 你可以直接看到你的微服务和持续产生的 issue 之间的依赖关系

如果你实现了我们刚刚讨论的这些方法和工具,Node.js 的日志记录将不再是一件难事。

function readLog(date,type,operation){//函数接受3个参数,日期、精度(天时分秒)和操作(查找、刷新或者默认)        if(date){        //如果有时间参数则对时间参数进行处理(一般日志名称都是时间加类型)            var dataStr = arguments[0]            var date = dataStr.slice(0,10)            var hour = dataStr.slice(11,13)            var minute = dataStr.slice(11,16)            var second =dataStr.slice(11,19)            var type = arguments[1]            //获取精度

        }else{        //若无时间参数则获取当前时间,然后精度默认            var date = new Date(+new Date()+8*3600*1000).toISOString().replace(/T/g,' ').replace(/\.[\d]{3}Z/,'').slice(0,10)            var type=null

        }        var op = arguments[2]        var countResult = 0        //对日志结果进行统计        var logPath = appDirName.slice(0,-3)+'log\\'+'ivsvr_'+date+'.log'        //拼接日志文件名称        var logStr = ''

        fs.stat(logPath, function (err, stats) {        //获取对应的日志文件状态        if(err){        //日志不存在或者异常处理信息

            $('#logTipInfo tr td').html('无日志信息可供查看')            console.log(err)

        }else{            var lineCount = 0            var readline = require('readline')            var fReadName = logPath            var fRead = fs.createReadStream(fReadName)            var objReadline = readline.createInterface({                input: fRead,

            })            //按行读取日志

            objReadline.on('line', (line)=>{                var itemStr = $.trim(line)                var time = itemStr.slice(0,19)                var content = itemStr.substr(20)                if(!time || !content){return}

                ++ lineCount                //计数行数                var matched = true                var itemStr = $.trim(line)                var itemArr = itemStr.split(' ')                var logDate = itemArr[0]                var logTime = itemArr[1]                var time = logDate+' '+logTime                switch(type){                //进度查找过滤                    case '0':                    case null:

                        matched = true                        break                    case '1':                        var timeSplit = logTime.slice(0,2)                        if(timeSplit == hour){

                            matched = true                            break

                        }

                        matched = false                        break                    case '2':                        var timeSplit = logTime.slice(0,5)                        if(timeSplit == minute){

                            matched = true                            break

                        }

                        matched = false                        break                    case '3':                        var timeSplit = logTime.slice(0,8)                        if(timeSplit == second){

                            matched = true                            break

                        }

                        matched = false                        break                    default:                        console.log('unkonw type!')                        break

                }                if(matched === true &&(lineCount>logReadFlag)){                //日志结果格式化输出呈现

                    countResult ++                    var result = itemArr[2]                    if(result == 'err'){                        var operation = itemArr[4]

                        result = '失败:'+itemArr[5]+' '+(itemArr[6]?itemArr[6]:'')

                    }else{                        var operation = itemArr[4]+' '+itemArr[5]+(itemArr[6]?itemArr[6]:'')

                        result = '成功'

                    }

                    logStr += '<tr><td>'+time+'</td><td>'+operation+'</td><td>'+result+'</td></tr>'

                }

                logStr += '<tr><td>'+time+'</td><td>'+content+'</td></tr>'

            })            objReadline.on('close', ()=>{                if(logReadFlag == 0 && (logStr == '')){                    var info = '无日志信息可供查看'

                }                if(logReadFlag >0 && (logStr != '')){                    var newLogNum = lineCount - logReadFlag                    var info = '刷新成功,更新'+newLogNum+'条日志!'

                    $('#logInfoTable').prepend(logStr)

                }else if(logReadFlag >0 && (logStr == '')){                    var info = '刷新成功,无日志更新!'

                }else{

                    $('#logInfoTable').html(logStr)

                }                if(op == 'search'){                    var info = '查找到'+countResult+'条日志'

                }

                $('#logTipInfo tr td').html(info)                if((op == 'search' || op == 'refresh') &&(logReadFlag != 0 || newLogNum >0 || countResult >0)){

                    setTimeout(function() {$('#logTipInfo').fadeOut(567)}, 2000)                    //操作结束后给出信息反馈,反馈自动淡出消失

                }                else{

                    $('#logTipInfo').hide()

                }

                logReadFlag = lineCount                console.log('closed')

            })

        }

    })

}