Log日志使用与规范

日志有什么用?

日志的作用是提供精准的系统记录方便根因分析。那么具体在哪些具体方面它可以发挥作用?

①打印调试: 即可以用日志来记录变量或者某一段逻辑。记录程序运行的流程,即程序运行了哪些代码,方便排查逻辑问题。

②问题定位: 程序出异常或者出故障时快速的定位问题,方便后期解决问题。因为线上生产环境无法 DEBUG,在测试环境去模拟一套生产环境,费时费力。
所以依靠日志记录的信息定位问题,这点非常重要。还可以记录流量,后期可以通过 ELK(包括 EFK 进行流量统计)。

③用户行为日志: 记录用户的操作行为,用于大数据分析,比如监控、风控、推荐等等。
这种日志,一般是给其他团队分析使用,而且可能是多个团队,因此一般会有一定的格式要求,开发者应该按照这个格式来记录,便于其他团队的使用。
当然,要记录哪些行为、操作,一般也是约定好的,因此,开发者主要是执行的角色。

④根因分析(甩锅必备): 即在关键地方记录日志。方便在和各个终端定位问题时,别人说是你的程序问题,你可以理直气壮的拿出你的日志说,看,我这里运行了,状态也是对的。这样,对方就会乖乖去定位他的代码,而不是互相推脱。

什么时候记录日志?

上文说了日志的重要性,那么什么时候需要记录日志?

①系统初始化: 系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录 INFO 日志,打印出参数以及启动完成态服务表述。

②编程语言提示异常: 如今各类主流的编程语言都包括异常机制,业务相关的流行框架有完整的异常模块。

这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用 WARN 或者 ERROR 级别。

③业务流程预期不符: 除开平台以及编程语言异常之外,项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。
取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。

④系统核心角色,组件关键动作: 系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录 INFO 级别日志。
比如电商系统用户从登录到下单的整个流程;微服务各服务节点交互;核心数据表增删改;核心组件运行等等,如果日志频度高或者打印量特别大,可以提炼关键点 INFO 记录,其余酌情考虑 DEBUG 级别。

⑤第三方服务远程调用: 微服务架构体系中有一个重要的点就是第三方永远不可信,对于第三方服务远程调用建议打印请求和响应的参数,方便在和各个终端定位问题,不会因为第三方服务日志的缺失变得手足无措。

Java日志实践

Java日志框架

  1. Log4jLog4j2
    Apache的开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;用户也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,用户能够更加细致地控制日志的生成过程。这些可以通过一个配置文件(XML或Properties文件)来灵活地进行配置,而不需要修改程序代码。Log4j2则是前任的一个升级,参考了Logback的许多特性;
  2. Logback
    Logback是由log4j创始人设计的又一个开源日记组件。logback当前分成三个模块:logback-core,logback-classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如log4j或JDK14 Logging;
  3. java.util.logging
    JDK内置的日志接口和实现,功能比较简单;
  4. Slf4j
    SLF4J是为各种Logging API提供一个简单统一的接口),从而使用户能够在部署的时候配置自己希望的Logging API实现;
  5. Apache Commons Logging
    Apache Commons Logging(JCL)希望解决的问题和Slf4j类似。

选项太多了的后果就是选择困难症,我的看法是没有最好的,只有最合适的。在比较关注性能的地方,选择Logback或自己实现高性能Logging API可能更合适;在已经使用了Log4j的项目中,如果没有发现问题,继续使用可能是更合适的方式;我一般会在项目里选择使用Slf4j, 如果不想有依赖则使用java.util.logging或框架容器已经提供的日志接口。

日志的基本格式

①日志时间

作为日志产生的日期和时间,这个数据非常重要,一般精确到毫秒。

yyyy-MM-ddHH:mm:ss.SSS

②日志级别

日志的输出都是分级别的,不同的设置不同的场合打印不同的日志。主要使用如下的四个级别:

DEBUG:DEUBG 级别的主要输出调试性质的内容,该级别日志主要用于在开发、测试阶段输出。

该级别的日志应尽可能地详尽,开发人员可以将各类详细信息记录到 DEBUG 里,起到调试的作用,包括参数信息,调试细节信息,返回值信息等等,便于在开发、测试阶段出现问题或者异常时,对其进行分析。

INFO:INFO 级别的主要记录系统关键信息,旨在保留系统正常工作期间关键运行指标,开发人员可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录到 INFO 日志中,方便日常运维工作以及错误回溯时上下文场景复现。

建议在项目完成后,在测试环境将日志级别调成 INFO,然后通过 INFO 级别的信息看看是否能了解这个应用的运用情况,如果出现问题后是否这些日志能提供有用的排查问题的信息。

WARN:WARN 级别的主要输出警告性质的内容,这些内容是可以预知且是有规划的,比如,某个方法入参为空或者该参数的值不满足运行该方法的条件时。在 WARN 级别的时应输出较为详尽的信息,以便于事后对日志进行分析。

ERROR:ERROR 级别主要针对于一些不可预知的信息,诸如:错误、异常等,比如,在 catch 块中抓获的网络通信、数据库连接等异常,若异常对系统的整个流程影响不大,可以使用 WARN 级别日志输出。

在输出 ERROR 级别的日志时,尽量多地输出方法入参数、方法执行过程中产生的对象等数据,在带有错误、异常对象的数据时,需要将该对象一并输出。

③DEBUG/INFO 的选择

DEBUG 级别比 INFO 低,包含调试时更详细的了解系统运行状态的东西,比如变量的值等等,都可以输出到 DEBUG 日志里。

INFO 是在线日志默认的输出级别,反馈系统的当前状态给最终用户看的。输出的信息,应该对最终用户具有实际意义的。

从功能角度上说,INFO 输出的信息可以看作是软件产品的一部分,所以需要谨慎对待,不可随便输出。

如果这条日志会被频繁打印或者大部分时间对于纠错起不到作用,就应当考虑下调为 DEBUG 级别:

由于 DEBUG 日志打印量远大于 INFO,出于前文日志性能的考虑,如果代码为核心代码,执行频率非常高,务必推敲日志设计是否合理,是否需要下调为 DEBUG 级别日志。
注意日志的可读性,不妨在写完代码 review 这条日志是否通顺,能否提供真正有意义的信息。
日志输出是多线程公用的,如果有另外一个线程正在输出日志,上面的记录就会被打断,最终显示输出和预想的就会不一致。

④WARN/ERROR 的选择

当方法或者功能处理过程中产生不符合预期结果或者有框架报错时可以考虑使用。

常见问题处理方法包括:

增加判断处理逻辑,尝试本地解决:增加逻辑判断吞掉报警永远是最优选择抛出异常,交给上层逻辑解决
抛出异常,交给上层逻辑解决
记录日志,报警提醒
使用返回码包装错误做返回

一般来说,WARN 级别不会短信报警,ERROR 级别则会短信报警甚至电话报警,ERROR 级别的日志意味着系统中发生了非常严重的问题,必须有人马上处理,比如数据库不可用,系统的关键业务流程走不下去等等。

错误的使用反而带来严重的后果,不区分问题的重要程度,只要有问题就 ERROR 记录下来。

其实这样是非常不负责任的,因为对于成熟的系统,都会有一套完整的报错机制,那这个错误信息什么时候需要发出来,很多都是依据单位时间内 ERROR 日志的数量来确定的。

⑤强调 ERROR 报警

ERROR 级别的日志打印通常伴随报警通知。ERROR 的报出应该伴随着业务功能受损,即上面提到的系统中发生了非常严重的问题,必须有人马上处理。

ERROR 日志目标:给处理者直接准确的信息,ERROR 信息形成自身闭环。

问题定位:

发生了什么问题,哪些功能受到影响
获取帮助信息:直接帮助信息或帮助信息的存储位置
通过报警知道解决方案或者找何人解决

⑥线程名称

输出该日志的线程名称,一般在一个应用中一个同步请求由同一线程完成,输出线程名称可以在各个请求产生的日志中进行分类,便于分清当前请求上下文的日志。

⑦opentracing 标识

在分布式应用中,用户的一个请求会调用若干个服务完成,这些服务可能还是嵌套调用的,因此完成一个请求的日志并不在一个应用的日志文件,而是分散在不同服务器上不同应用节点的日志文件中。

该标识是为了串联一个请求在整个系统中的调用日志:

唯一字符串(trace id)
调用层级(span id)

通过搜索 trace id 就可以查到这个 trace id 标识的请求在整个系统中流转(处理)过程中产生的所有日志。

⑧biz 标识

在业务开发中,我们的日志都是和业务相关联的,有时候是需要根据用户或者业务做聚类的,因此一次请求如果可以通过某项标识做聚类的时候,可以将聚类标识打印到日志中:

用户标识(user id)
业务标识(biz id)

⑨日志记录器名称

日志记录器名称一般使用类名,日志文件中可以输出简单的类名即可,看实际情况是否需要使用包名和行号等信息。主要用于看到日志后到哪个类中去找这个日志输出,便于定位问题所在。

⑩日志内容

禁用 System.out.println 和 System.err.println。

变参替换日志拼接,输出日志的对象,应在其类中实现快速的 toString 方法,以便于在日志输出时仅输出这个对象类名和 hashCode。

预防空指针:不要在日志中调用对象的方法获取值,除非确保该对象肯定不为 null,否则很有可能会因为日志的问题而导致应用产生空指针异常。

⑪异常堆栈

异常堆栈一般会出现在 ERROR 或者 WARN 级别的日志中,异常堆栈含有方法调用链的系统,以及异常产生的根源。异常堆栈的日志属于上一行日志的,在日志收集时需要将其划至上一行中。

Error规范

  1. 输出Exceptions的全部Throwable信息,因为logger.error(msg)和logger.error(msg,e.getMessage())这样的日志输出方法会丢失掉最重要的StackTrace信息。
  2. 不允许记录日志后又抛出异常,因为这样会多次记录日志,只允许记录一次日志。
  3. 不允许出现System print(包括System.out.println和System.error.println)语句。
  4. 不允许出现printStackTrace。

ELK 通用日志解决方案

ELK 是 Elasticsearch、Logstash、Kibana 三大开源框架首字母大写简称。市面上也被成为 Elastic Stack。

其中 Elasticsearch 是一个基于 Lucene、分布式、通过 Restful 方式进行交互的近实时搜索平台框架。

像类似百度、谷歌这种大数据全文搜索引擎的场景都可以使用 Elasticsearch 作为底层支持框架,可见 Elasticsearch 提供的搜索能力确实强大,市面上很多时候我们简称 Elasticsearch 为 ES。

Logstash 是 ELK 的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/Redis/Elasticsearch/Kafka 等)。

Kibana 可以将 Elasticsearch 的数据通过友好的页面展示出来,提供实时分析的功能。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值