城市直播房产教育博客汽车
投稿投诉
汽车报价
买车新车
博客专栏
专题精品
教育留学
高考读书
房产家居
彩票视频
直播黑猫
投资微博
城市上海
政务旅游

如何设计日志内容

5月25日 碧落盟投稿
  概述
  本文主要为需要进行软件开发的读者提供一些经验和建议。
  面向读者需要进行软件开发的程序员需要所开发系统进行整体把控的软件工程师
  1。有关日志的基本讨论
  在介绍如何设计日志内容前,有以下几个问题需要进行一些简单讨论。本文的全部内容都基于以下这些讨论来编写。
  1。1为什么要输出日志?
  答:为了定位问题。
  有人认为,系统在运行时如果遇到运行时错误时,本身编程语言或者虚拟机层面就会抛出错误,并且会标记具体的代码行号,所在函数等。并不需要额外去输出日志。也有人认为,系统在上线前,就已经经过详细的测试,所有的处理都已经被检验过,没有必要再在日志中输出相关内容。还有人认为,系统上线后出问题还是自己去负责修复,别人根本也不会去看日志,因此输不输出日志都无所谓。
  这些想法都是比较片面的。
  我们提到问题,不仅仅是系统报错,也可以是和设计不符的处理过程,甚至可能是用户不肯承认所做的操作而寻找的证据。其次,虽然系统在上线前理应被完整地测试,但测试工作本身也可能有出错,有遗漏。如果去要求测试100没有疏忽的话,不如去要求开发100没有Bug来的更加直接。最后,开发一个系统的工作并不仅仅只有开发,测试、运维甚至后续运营,好的日志不仅为自身开发带来便利,对其他岗位也有帮助,可以提高团队的整体运行效率。反过来,其他岗位在反馈问题时,如果有日志可查可附带,对开发本身也能带来极大的便利。
  因此,无论一个系统多么完美,是否被详细测试过,输出日志都是必不可少的工作。
  1。2日志输出了给谁看?
  答:所有与这个系统相关的人,都需要查看全部或者部分日志。
  有人认为,日志只是自己开发调试,以及后续对线上Bug调查时自己看的。反正代码最后都是自己来改,那么其他人不需要看,也没必要看。
  这个想法也是错误的。
  和编写代码除了自己能看懂,也要让别人也能看懂一样。如果说代码仅限开发岗需要看的话,那需要看日志的岗位就更多了。和上一问中提到的,测试、运维甚至运营都是有可能需要查看全部或者部分日志,并为他们的工作提供帮助。
  岗位
  日志用途
  开发
  开发调试、定位故障
  测试
  提交Bug的依据
  了解程序运行关键点,设计更有针对性的测试用例
  运维
  反馈问题的依据
  确定并排除基础问题(如配置错误导致系统无法启动等)
  运营
  查看具体用户操作
  帮助解决用户纠纷等
  因此,一份越多人能看得懂、用得上的日志,是我们应当追求的目标。
  1。3不输出日志或者日志内容随意有什么危害?
  答:各种想得到或者想不到的问题都会接踵而来
  结合上面两问,这个问题的解答就变得显而易见了。
  岗位
  不打日志的危害
  开发开发过程遇到问题难以定位
  开发加班
  测试测试提交Bug描述笼统
  开发需要重现Bug
  没有日志问题难以定位
  加班
  测试过程全黑盒
  测试无法设计出完备的测试用例
  系统上线后出现故障
  没有日志问题难以定位
  开发、测试加班
  运维出现任何问题都无法自行排查
  凡事都找开发
  没有日志问题难以定位
  开发、运维加班
  运营用户在使用过程中遇到问题
  用户并不记得或者不知道做了什么操作
  凡事都找开发
  没有日志问题难以定位
  开发加班
  相信通过以上日志三问,读者应该能够理解本文有关日志的观点。接下来,就具体看一下如何输出日志。
  2。日志格式
  一般来说,一行日志即一行文本。虽然日志本身并没有严格要求格式,但规范、统一的日志输出格式不仅可以方便阅读,也能便于各种日志系统搜集提取。
  2。1纯文本日志
  纯文本日志是最常见,也最简单的日志输出形式。绝大多数编程语言中,都可以简单地print输出日志。
  优点是可以完全自行规划格式,缺点就是不利于日志系统搜集提取。
  一段典型的纯文本日志如下:〔I〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔ANONYMITY〕〔ANONYMITY〕〔0ms〕〔0ms〕〔REQUEST〕hostweb01:8088,methodGET,urlusers,query{pageSize:100},originalUrlusers?pageSize100〔I〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔ANONYMITY〕〔ANONYMITY〕〔1ms〕〔0ms〕〔REQUESTFROM〕ip172。16。35。1,ips,remoteip,xrealip,xforwardedfor,xforwardedproto,refererhttp:localhost:8081,browserChrome,version96。0。4664。93,osOSX,platformAppleMac〔I〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔uadmin〕〔admin〕〔1ms〕〔3ms〕Authby〔usernamepassword〕:usernameadmin〔I〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔uadmin〕〔admin〕〔1ms〕〔4ms〕〔REQUESTUSER〕系统管理员(uadmin)〔D〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔uadmin〕〔admin〕〔1ms〕〔5ms〕〔MID〕INrequestValidator〔D〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔uadmin〕〔admin〕〔2ms〕〔7ms〕〔MID〕INuserModule〔D〕〔121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔uadmin〕〔admin〕〔2ms〕〔9ms〕〔RESPONSE〕JSON:{ok:true,error:200,message:,data:10Records,pageInfo:{pagingStyle:normal,pageSize:100,count:10,totalCount:10,pageNumber:1,pageCount:1,isFirstPage:true},traceId:TRACEFD8CDE8878AB47E4886A85681700520B,reqTime:20211213T10:54:44。896Z,respTime:20211213T10:54:44。905Z,reqCost:9},Length:2794
  这段日志主要分为2部分:
  1。自动生成的标签部分(〔I〕〔2021121318:54:44〕〔web01〕〔TRACEFD8CDE88〕〔ANONYMITY〕〔ANONYMITY〕〔0ms〕〔0ms〕)
  2。具体编码输出内容部分(〔REQUESTUSER〕系统管理员(uadmin))
  这段日志主要诉求是方便阅读。因此,日志等级(〔I〕)、时间戳(〔121318:54:44〕)、跟踪ID(TRACEFD8CDE88)都做了缩短处理
  2。2JSON格式日志
  把日志以JSON格式输出时,一般主要是为了方便日志系统搜集的考量。
  优点自然是日志系统搜集方便,缺点是直接阅读时不太直观。
  以上文中2。1中的日志为例,以JSON格式输出时内容如下:{level:INFO,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:ANONYMITY,username:ANONYMITY,diffTime:0,costTime:0,message:〔REQUEST〕hostweb01:8088,methodGET,urlusers,query{pageSize:100},originalUrlusers?pageSize100}{level:INFO,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:ANONYMITY,username:ANONYMITY,diffTime:1,costTime:0,message:〔REQUESTFROM〕ip172。16。35。1,ips,remoteip,xrealip,xforwardedfor,xforwardedproto,refererhttp:localhost:8081,browserChrome,version96。0。4664。93,osOSX,platformAppleMac}{level:INFO,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:uadmin,username:admin,diffTime:1,costTime:3,message:Authby〔usernamepassword〕:usernameadmin}{level:INFO,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:uadmin,username:admin,diffTime:1,costTime:4,message:〔REQUESTUSER〕系统管理员(uadmin)}{level:DEBUG,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:uadmin,username:admin,diffTime:1,costTime:5,message:〔MID〕INrequestValidator}{level:DEBUG,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:uadmin,username:admin,diffTime:2,costTime:7,message:〔MID〕INuserModule}{level:DEBUG,timestamp:20211213T10:54:44Z,hostname:web01,traceId:TRACEFD8CDE8878AB47E4886A85681700520B,userId:uadmin,username:admin,diffTime:2,costTime:9,message:〔RESPONSE〕JSON:{ok:true,error:200,message:,data:10Records,pageInfo:{pagingStyle:normal,pageSize:100,count:10,totalCount:10,pageNumber:1,pageCount:1,isFirstPage:true},traceId:TRACEFD8CDE8878AB47E4886A85681700520B,reqTime:20211213T10:54:44。896Z,respTime:20211213T10:54:44。905Z,reqCost:9},Length:2794}
  2。3纯文本日志vs。JSON格式日志
  纯文本日志与JSON格式都有各自的优缺点。具体可以根据实际情况进行选择。
  比如,开发的系统规模较小,系统为单体应用,没有复杂的处理,没有引入第三方日志系统等情况下,那纯文本足矣。而对于分布式系统、微服务场景、已经搭建好第三方日志系统的,则可以考虑使用JSON格式输出。
  如果条件允许,可以改造业务系统的日志模块,使其可以根据配置输出不同格式的日志。实现在本地开发,不接入第三方日志系统时输出纯文本日志,而生产环境下则输出JSON格式日志。
  3。日志标签
  在之前的例子里可以看到,除了在编码时指定的输出内容外,每条日志都具有相同字段的标签,这些通过这些标签可以区分每行日志的来源等基本信息。
  可以说,没有标签的日志几乎没有任何价值。比如,在Web应用中,必然同时有多个用户、多个请求在访问系统。设想下面2个请求并发场景下输出的日志:〔TRACE0000001〕GETusers〔TRACE0000002〕GETbooks〔TRACE0000002〕RESPONSE10records〔TRACE0000001〕RESPONSE3records
  那么,通过traceId可以很清楚得看到,GETusers接口返回了3条数据,而GETbooks接口返回了10条数据。
  如果没有traceId标签,那么我们看到的日志就会像下面这样:GETusersGETbooksRESPONSE10recordsRESPONSE3records
  虽然和之前相比输出了同样内容的日志,但由于缺少traceId,大片日志完全混在了一起,即使我们去检查日志,只能非常孤立得去看每一行日志,而无法区分来自同一个请求的上下文日志。那么,这份日志显然用处就不大了。
  下面举了一些常用的日志标签,根据实际情况,可以选择不同的组合来为每行日志加上标签,使得输出的日志可搜索、可筛选、可跟踪。
  重要性
  字段名
  说明
  appName
  应用名称
  moduleName
  模块名称
  upTime
  系统自启动起经过时间
  重要
  level日志等级,如:DEBUG
  ,INFO
  ,WARNING
  ,ERROR
  levelShort日志等级简写,如:D
  ,I
  ,W
  ,E
  重要
  timestamp
  时间戳(秒),如:1503936000
  timestampMs
  时间戳(毫秒),如:1503936000000
  重要
  timestampHumanized
  时间戳(可读格式),如:2017082900:00:00
  timestampHumanizedShort
  时间戳(可读简化格式),如:082900:00:00
  hostname
  所在主机名
  重要traceId
  requestId
  跟踪请求ID,可以为UUID4,如:FD8CDE8878AB47E4886A85681700520B
  traceIdShort
  requestIdShort
  跟踪请求ID简写,可以为UUID第一段,如FD8CDE88
  重要
  userId
  用户ID,未登录用户可以固定为ANONYMITY
  重要
  username
  用户名,未登录用户可以固定为ANONYMITY
  clientId
  客户端ID,浏览器可以通过在Cookies中写入实现
  clientIP
  客户端IP地址
  diffTime
  本条日志与上一条日志之间的时间间隔(毫秒),可用于简单判断耗时较长的代码范围
  重要
  costTime
  本次请求从开始到本条日志之间的时间间隔(毫秒),可用于简单判断耗时较长的代码范围
  3。1日志等级
  上面提到的各种日志标签都比较好理解,这里需要单独提一下日志等级。
  日志等级是一个非常容易产生混乱的部分。有些系统对日志等级的划分比较随意,甚至有时只是随便填写,比如不管什么日志都是DEBUG,任何错误都是ERROR。这么做会对后续查看、分析日志产生迷惑性,也会对第三方日志系统产生干扰。
  为了合理设定每条日志的等级,首先我们得先确定一共有多少级别以及各自对应的问题严重程度。
  日志级别
  1hr致命FATAL
  2hr严重CRITICAL
  3hr错误ERROR
  4hr警告WARNING
  5hr信息INFO
  6hr调试DEBUG
  7hr跟踪TRACE
  一般我们能见到的所有日志级别大概就是以上7种,但在具体不同的系统中,级别总数、划分方式会有所区别。
  过多的级别也会增加开发负担,此外,各个级别所对应的内容也并没有严格的规定。在这里我们假设一个Web系统,其日志模块包含5个级别(CRITICAL、ERROR、WARNING、INFO、DEBUG)作为例子来进行说明。
  严重CRITICAL
  完全无法预期的问题,错误会导致处理无法执行下去,甚至可能被抛出至运行时层面导致系统崩溃重启,如:系统相关组件、包、配置文件缺失获取本应存在的数据,但实际获取时数据缺失执行SQL语句失败
  产生此类问题时,如果系统依然可以返回HTTP响应,一般返回5xx服务器错误。
  这个级别的问题必然是系统Bug,因此任何属于这类的问题都需要立刻被修复。
  错误ERROR
  导致处理无法执行下去的问题,但不至于导致系统崩溃,错误也能在处理逻辑中捕捉,如:调用第三方API报错连接数据库失败接口调用参数不正确用户认证令牌过期导致认证失败业务上不允许的操作,如重复录入数据等
  产生此类问题时,一般系统依然可以返回HTTP响应,返回的同样也是5xx服务器错误。
  这个级别的问题一般源于外部系统,并非本系统Bug。因此可能谈不上修复,更多的是优化,如:调用第三方API失败时增加重试机制优化UI避免用户输入非法内容或进行不允许的操作等。
  警告WARNING
  一些算不上问题的问题,如:访问不存在的路由、数据(即404,如GETfavicon。ico)各种用户名、密码、验证码错误
  这类问题往往并不需要去修复,只要定期查看是否数量暴涨即可。产生此类问题的源头往往是爬虫系统、各种健康检查、漏洞扫描,但一般来说数量稳定,不太容易出现暴涨暴跌的情况。
  信息INFO
  单纯记录提示信息,如:〔INFO〕〔2021010100:00:00〕〔张三〕创建了目录favbooks〔INFO〕〔2021010100:00:00〕〔张三〕上传了文件favbooksa。txt(20KB)〔INFO〕〔2021010100:00:00〕〔张三〕删除了目录favbooks
  这个级别可以在测试环境中用于测试过程的跟踪,也可以在系统上线后作为用户数据分析,当然,也可以完全不输出此类日志。
  调试DEBUG
  面向开发、测试级别的信息,在这类信息中会有详细的变量内容输出来帮助对程序进行调试,具体内容可参考下文。
  4。日志内容
  日志内容即在编码时指定的输出内容,这部分内容相对自由,但一般也有一些技巧可以使用。
  注意:为了便于理解,以下示例代码均以Javascript,使用console。log()输出为例。日志内容本身与具体所用语言框架并无直接关联性
  4。1输出变量
  在输出日志内容时,输出关键变量的内容是最常见的做法。但在实际项目中,很多人输出日志其实并不非常到位。
  一个比较完备的变量输出例子如下:varmyVar1true布尔值的truevarmyVar2true字符串的truevarmyVar3true字符串的true,尾部有空格console。log(myVar1{JSON。stringify(myVar1)})console。log(myVar2{JSON。stringify(myVar2)})console。log(myVar3{JSON。stringify(myVar3)})console。log(myVar3〔{myVar3}〕)
  输出如下:myVar1truemyVar2truemyVar3truemyVar3〔true〕
  输出变量名
  输出变量应当总是输出对应的变量名。
  很多人习惯直接使用类似console。log(myVar1)来输出日志,当日志量少的时候可能问题不大,当涉及循环、多个条件的复杂分支、随机数等,如果没有变量名作为提示,就很容易产生看不懂、看错等问题。如:错误示例varmyVar1parseInt(Math。random()10)varmyVar2parseInt(Math。random()10)varmyVar3parseInt(Math。random()10)console。log({JSON。stringify(myVar1)})console。log({JSON。stringify(myVar2)})console。log({JSON。stringify(myVar3)})错误示例
  输出如下:869
  这种情况下,如果不对照这代码一行一行对比,根本没法知道各个变量是什么值
  输出的变量值应当能表明类型
  在输出变量值时,最容易混淆的就是,数字1和字符串1、布尔值true和字符串true这类问题,并且在Javascript、Python中最常见。忽略此问题有时会让日志内容看上去非常奇怪。如:错误示例varisOKfalseconsole。log(isOK{isOK})if(isOK){console。log(ItsOK!)}错误示例
  输出如下:isOKfalseItsOK!
  这种情况下,日志中明明isOK变量输出为假,但依然进入了分支内,非常诡异!
  以Javascript为例,可以有多种方法将变量值类型表示出来:varisOKfalseconsole。log(isOK({typeofisOK}){isOK})将变量值类型直接输出console。log(isOK{JSON。stringify(isOK)})将变量值序列化varisOKfalseconsole。log(isOK({typeofisOK}){isOK})将变量值类型直接输出console。log(isOK{JSON。stringify(isOK)})将变量值序列化
  输出如下:isOK(string)falseisOKfalseisOK(boolean)falseisOKfalse
  输出的字符串应当能表明前后空格
  在输出变量值时,另一个容易令人产生迷惑的就是字符串前后空格和不可见字符了。忽略此问题也会让日志看上去非常奇怪,如:错误示例vartypemainconsole。log(type{type})if(typemain){console。log(ItsMain!)}else{console。log(ItsNOTMain!)}错误示例
  输出如下:typemainItsNOTMain!
  这种情况下,日志中明明type变量输出为main,但依然进入了其他分支内,非常诡异!
  以Javascript为例,可以有多种方法将字符串前后空格表示出来:vartypemainconsole。log(type〔{type}〕)前后加入头尾标志符console。log(type{JSON。stringify(type)})按照JSON序列化(字符串需要包裹
  输出如下:type〔main〕typemain
  输出的日期时间应当保留时区
  在开发过程中,日期也是出问题的重灾区,由于开发本地机器、服务器所配置的时区可能并不相同,非常容易在日志中输出一些并不明确的日期,如:错误示例vartimenewDate()vartimeStr(0time。getHours())。slice(2):(0time。getMinutes())。slice(2):(0time。getSeconds())。slice(2)console。log(time{timeStr})错误示例
  输出如下:time03:10:02
  虽然输出的时间是正确的,但是很难判断这里的时间到底是北京时间还是UTC时间,不利于问题的排查
  对于Javascript来说,输出符合ISO8601标准的日期非常简单,如:vartimenewDate()console。log(time{time。toISOString()})
  输出如下:time20211213T19:13:35。793Z
  4。2调用关键函数、进入分支前输出日志
  在了解如何输出变量之后,接下来就要考虑选择什么样的变量输出。
  一般来说,关键函数、分支的判断依据需要输出,用来判断程序走向来定位问题。而程序处理过程中的大量中间变量则不需要输出,在开发时完全可以使用Debug单步调试等查看内容,如:vartypemailconsole。log(〔INFO〕根据类型发送不同的日志内容:type{JSON。stringify(type)})if(typemail){console。log(〔INFO〕发送邮件)}elseif(typesms){console。log(〔INFO〕发送短信)}
  输出如下:〔INFO〕根据类型发送不同的日志内容:typemail〔INFO〕发送邮件
  4。3控制每行日志长度
  由于输出日志本身也会消耗系统的资源,同时,过长的单行日志本身也可能存在设计上的问题。因此应当控制好每行日志的最大长度,避免为了日志完整而无脑输出日志,如:错误示例longMsgBhlabala。repeat(100)console。log(〔INFO〕longMsg)错误示例
  输出如下:〔INFO〕BhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlabala
  日志过长,不仅消耗系统资源,同时也难以阅读
  因此,对于长内容,可以只输出关键部分,或者最开始部分等,避免无意义的超长日志,如:longMsgBhlabala。repeat(100)console。log(〔INFO〕longMsg。slice(0,50)。。。(Length:{longMsg。length}))
  输出如下:〔INFO〕BhlabalaBhlabalaBhlabalaBhlabalaBhlabalaBhlab。。。(Length:900)
  4。4内容包含换行时的处理
  有些日志内容本身包含了我们无法控制的换行,如报错时需要输出的函数调用堆栈列表。在JSON格式下整个内容都在message字段中并不会有什么问题,但如果以纯文本方式输出的话,会导致日志内容割裂,如:错误示例try{varsHellos。run()}catch(err){console。log(〔ERROR〕〔{newDate()。toISOString()}〕,err。stack)}错误示例
  输出如下:〔ERROR〕〔20211213T18:23:10。085Z〕TypeError:s。runisnotafunctionatObject。(Userspastgiftgitpastgifttrytest。js:3:5)atModule。compile(internalmodulescjsloader。js:955:30)atObject。Module。extensions。。js(internalmodulescjsloader。js:991:10)atModule。load(internalmodulescjsloader。js:811:32)atFunction。Module。load(internalmodulescjsloader。js:723:14)atFunction。Module。runMain(internalmodulescjsloader。js:1043:10)atinternalmainrunmainmodule。js:17:11
  这时,无论是直接阅读,第三方日志系统搜集,还是单纯grep命令过滤日志文件,都非常容易被换行干扰
  这时,可以对包含换行的日志内容按照换行符每行单独输出,保证每行都有标签,如:try{varsHellos。run()}catch(err){err。stack。split()。forEach(l{console。log(〔ERROR〕〔{newDate()。toISOString()}〕,l)})}
  输出如下:〔ERROR〕〔20211213T18:27:01。964Z〕TypeError:s。runisnotafunction〔ERROR〕〔20211213T18:27:01。969Z〕atObject。(Userspastgiftgitpastgifttrytest。js:3:5)〔ERROR〕〔20211213T18:27:01。969Z〕atModule。compile(internalmodulescjsloader。js:955:30)〔ERROR〕〔20211213T18:27:01。969Z〕atObject。Module。extensions。。js(internalmodulescjsloader。js:991:10)〔ERROR〕〔20211213T18:27:01。969Z〕atModule。load(internalmodulescjsloader。js:811:32)〔ERROR〕〔20211213T18:27:01。969Z〕atFunction。Module。load(internalmodulescjsloader。js:723:14)〔ERROR〕〔20211213T18:27:01。969Z〕atFunction。Module。runMain(internalmodulescjsloader。js:1043:10)〔ERROR〕〔20211213T18:27:01。969Z〕atinternalmainrunmainmodule。js:17:11
  5。后记
  良好的日志输出不仅可以为自己和同事的工作提供帮助,同时也是系统可观测的重要一环。
  本文中所提到的方法、设计不可能符合所有的系统,这里仅作为参考推荐给大家,希望能给大家提供有用的帮助。
投诉 评论 转载

如何设计日志内容概述本文主要为需要进行软件开发的读者提供一些经验和建议。面向读者需要进行软件开发的程序员需要所开发系统进行整体把控的软件工程师1。有关日志的基本讨论在介……鲁迅藤野先生,让我永世难忘!藤野谁是鲁迅?哦,那个中等生鲁迅一名几乎无人不晓,大多数人都知晓他从学医向写作的转变过程,这位先生所著作的文章至今还被认作教科书的经典模范,连同他的经历也一起被人们传颂。但凡是对鲁迅先生了解较深的人……ampampquot权力斗争背叛国家金兵入侵大宋历史上具争议秦桧被誉为大宋历史上的第一位杰出的相,他统治宋朝十九年,被宋朝尊为忠臣,死后,宋高宗皇帝特别颁发决策元功,精忠全德,封他为申王,并且给予忠献的谥号。秦桧和挞懒在建炎三年的……外媒中国要放飞第一架大型客机,不过含金量不足中国想要自己的大型客机。在花了几十年时间填足了海外制造商波音公司的钱袋子之后,中国在2006年的五年计划中决定要不惜一切代价,为世界上增长最快的航空旅行市场之一提供自己的国产飞……孩子4岁多,回家支支吾吾说幼儿园老师让我把打翻在地上的饭全吃作为一名幼教工作者针对这样的孩子在家给你说出这样一句话,作为家长的第一反应肯定是惊慌的,但又不知道怎么办才合适,可以给你支点招。1。不要去逼迫孩子继续说出原因,因为4岁多……没有分享欲,谈什么恋爱当一个人没有了跟你分享的欲望,那也是感情走到陌路的时候!我一直觉得分享是一件很浪漫的事情,大到余生共享,小到一句我在喝水水,你要不要喝?一本书,一个电影,一场日落,……商用车销量降,原材料价格涨,江淮汽车上半年巨亏7。12亿元实习记者冯雨晨8月初股价暴涨了一波的江淮汽车(600418。SH),上半年业绩略带寒意。8月29日晚,江淮汽车公告,上半年实现营业收入177。72亿元,同比下降20……三涨停紧接两跌停!又见LOF大走过山车,这类情况需警惕中国基金报记者方丽实习生王佳霖一只LOF基金在连续三个涨停之后马上来了两大跌停!最近市场出现这一幕引起行业关注。数据显示,有一些LOF的二级市场交易价格坐上过……2022大学生就业季高校对接地方产业用人需求拓展就业机会央视网消息:关注大学生就业。当前,正值2022届高校毕业生就业冲刺阶段。各地各高校纷纷创新工作模式,千方百计促就业,积极对接地方产业需求,想方设法开拓就业渠道,为毕业生顺利就业……南粤金融租赁在澳门落地融资租赁业再添新军南方财经全媒体记者田静发自澳门10月26日,南粤金融租赁(澳门)股份有限公司(下称南粤金融租赁)在澳门举行开业揭牌仪式,南粤金融租赁是澳门融资租赁法律修订后首个按新规申请落地的……咸猪手何时休?在公共场合遭遇咸猪手你会怎么办?是忍气吞声还是逆来顺受。近日,网上一则男子高铁上疑似咸猪手遭大姐暴打的视频刷爆网络,在为这位大妈敢于站出来和这种违法犯罪行为作斗争的同时,我也想……普京刚降低天然气输送量,欧洲电网就要崩了,今年冬天用爱取暖吗进入7月份以来,一股热浪席卷了整个北半球,导致一向气候温和的欧洲也进入了烧烤模式。在7月19日这天,向来以凉爽海洋性气候著称的英国,首次达到了40摄氏度的历史极值,这对很少体验……
小松鼠和小兔子虚构造句用虚构造句大全两会刚刚闭幕,不出意外的话,老百姓的生活将发生如下三点变化!翰海春拍佳作美器熠烁拍场漱口水能改善口臭吗我最伟大的老师与工作无关的小憩无助于提高生产力王艺迪是本届世乒赛我女乒的一大功臣吗?旅游古巴vivoX90系列曝光或将首发天玑9200,还有一英寸大底?职称不再困于论文让中小学老师拼真章企业如何打好免费牌

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找江西南阳嘉兴昆明铜陵滨州广东西昌常德梅州兰州阳江运城金华广西萍乡大理重庆诸暨泉州安庆南充武汉辽宁