关于java:你真的会正确使用日志吗

43次阅读

共计 4551 个字符,预计需要花费 12 分钟才能阅读完成。

日志在应用程序中是十分十分重要的,好的日志信息能有助于咱们在程序呈现 BUG 时能疾速进行定位,并能找出其中的起因。

然而,很多介绍 AOP 的中央都采纳日志来作为介绍,实际上日志要采纳切面的话是极其不迷信的!

对于日志来说,只是在办法开始、完结、异样时输入一些什么,那是相对不够的,这样的日志对于日志剖析没有任何意义。

如果在办法的开始和完结整个日志,那办法中呢?

如果办法中没有日志的话,那就齐全失去了日志的意义!

如果利用呈现问题要查找由什么起因造成的,也没有什么作用。

这样的日志还不如不必!

心愿藉以本文能让应用程序的开发人员能更加器重日志,能在利用中输入有意义的日志。

日志根本格局

日志输入次要在文件中,应包含以下内容:

  • 工夫
  • 日志级别次要应用
  • 调用链标识(可选)
  • 线程名称
  • 日志记录器名称
  • 日志内容
  • 异样堆栈(不肯定有)

    11:44:44.827 WARN [93ef3E0120160803114444-1.2] [main] [ClassPathXmlApplicationContext] Exception encountered during context initialization – cancelling refresh attempt

日志工夫

作为日志产生的日期和工夫,这个数据十分重要,个别准确到毫秒。

因为个别按天滚动日志文件,日期不须要放在这个工夫中,应用 HH:mm:ss.SSS 格局即可。

日志级别

日志级别次要应用 DEBUG、INFO、WARN、ERROR。

DEBUG

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

该级别的日志应尽可能地详尽,便于在开发、测试阶段呈现问题或者异样时,对其进行剖析。

INFO

INFO 级别的次要输入提醒性质的内容,该级别日志次要用于生产环境的日志输入。

该级别或更高级别的日志不要呈现在循环中,能够在循环开始或者完结后输入循环的次数,以及一些其余重要的数据。

  • 利用启动时所加载的配置参数值(比方:连贯参数、线程池参数、超时工夫等,以及一些与环境相干的配置,或者是整个配置参数)
  • 一些重要的依赖注入对象的类名
  • 办法(服务办法)的输出参数值、返回值,因为一些办法入参的值十分多,只在入口处输入一次就能够了,在服务办法外部或者调用非服务办法时就不须要再输入了
  • 办法中重要的局部,比方:从数据库中所获取较为重要的数据,以及调用第三方接口的输出参数值和接口返回值

INFO 级别日志准则是在生产环境中,通过 INFO 和更高级别的日志,能够理解零碎的运行状况,以及呈现问题或者异样时,能疾速地对问题进行定位,还原过后调用的上下文数据,能重现问题。

倡议在我的项目实现后,在测试环境将日志级别调成 INFO,而后通过 INFO 级别的信息看看是否能理解这个利用的使用状况,如果呈现问题后是否这些日志是否提供有用的排查问题的信息。

WARN

WARN 级别的次要输入正告性质的内容,这些内容是能够预知且是有布局的,比方,某个办法入参为空或者该参数的值不满足运行该办法的条件时。

在 WARN 级别的时应输入较为详尽的信息,以便于预先对日志进行剖析,不要间接写成:

不好的日志

log.warn(``"name is null"` `);
复制代码 

除了输入正告的起因之外,还须要将其余参数内容都输入,以便于有更多的信息供为日志剖析的参考。

举荐的日志

log.warn(``"[{}] name is null, ignore the method, arg0: {}, arg1: {}"` `, methodName , arg0 , arg1 );
复制代码 

ERROR

ERROR 级别次要针对于一些不可预知的信息,诸如:谬误、异样等,比方,在 catch 块中抓获的网络通信、数据库连贯等异样,若异样对系统的整个流程影响不大,能够应用 WARN 级别日志输入。

在输入 ERROR 级别的日志时,尽量多地输入办法入参数、办法执行过程中产生的对象等数据,在带有谬误、异样对象的数据时,须要将该对象一并输入:

举荐的日志

log.error("Invoking com.service.UserService cause error, username: {}" , username , e );
复制代码 

不要写成(上面这种会将 e 作为日志内容参数中的一个,成果与应用 e.toString() 统一,不会输入异样堆栈):

不好的日志

log.error(``"Invoking com.service.UserService cause error, username: {}, e: {}"` `, username , e);
复制代码 

不要在日志中输入上面这样的日志,在异样堆栈 e 中自身就会输入 e.getMessage 的内容,没必要在日志行中输入一遍,这样的日志对于问题的追踪毫无意义!

不好的日志

log.error(e.getMessage() , e );
复制代码 

调用链标识

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

该标识是为了串联一个申请在整个零碎中的调用日志。

调用链标识格局:

  • 惟一字符串(trace ID)
  • 调用层级(span ID)

调用链标识作为可选项,无该数据时只输入 [] 即可。

线程名称

输入该日志的线程名称,个别在一个利用中一个同步申请由同一线程实现,输入线程名称能够在各个申请产生的日志中进行分类,便于分清以后申请上下文的日志。

日志记录器名称

日志记录器名称个别应用类名,日志文件中能够输入简略的类名即可,看理论状况是否须要应用包名。

次要用于看到日志后到哪个类中去找这个日志输入,便于定位问题所在。

日志内容

注意事项

禁用 System.out.println

src/main 的代码中严禁应用 System.out.println 进行输入,因为生产环境个别不会将规范输入和谬误输入重定向到文件中去,如果代码中应用该形式输入日志,可能会导致该输入失落。

变参替换日志拼接

应用 slf4j 的 Logger 进行解决,应用其变参性能进行日志输入,不要在日志中进行字符串的拼接,比方:

举荐的日志

log.debug("Load No.{} object, {}" , i , object);
复制代码 

不要写成 log.debug (“Load No.” + i + ” object, ” + object); 这是因为将日志级别调至 INFO 或以上级别时,这样会减少无畏的字符串拼接。

实现 toString()

须要输入日志的对象,应在其类中实现疾速的 toString 办法,以便于在日志输入时仅输入这个对象类名和 hashCode。

该 toString 办法应该解决类中所有的字段。

toString 办法能够通过 IDE 的主动性能 toString 性能生成。

toString 办法倡议不要通过反射或者一些 toString 工具类生成,也不要间接应用 JSON 序列化工具转为 JSON 字符串,这两者均应用反射进行解决的,仅为了输入日志较为影响利用的性能。

预防空指针

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

不好的日志

log.debug("Load student(id={}), name: {}" , id , student.getName() );
复制代码 

能够改为(当 student 为 null 时,这样也不会产生空指针异样):

举荐的日志

log.debug("Load student(id={}), student: {}" , id , student);
复制代码 

对于一些肯定须要进行拼接字符串,或者须要消耗工夫、节约内存能力产生的日志内容作为日志输入时,应应用 log.isXxxxxEnable() 进行判断后再进行拼接解决,比方:

举荐的代码

if (log.isDebugEnable() ) {``  StringBuilder builder = new StringBuilder();``  for (Student student : students) {``    builder.append( "student:").append(student);``  }``  builder.append("value:").append(JSON.toJSONString(object) );``  log.debug("debug log example, detail: {}" , builder );``}
复制代码 

信息安全

切记不要 log 明码及个人信息相干的内容!为了便于进行问题定位,以下是波及敏感信息日志输入时最为宽松(明文显示的数据只能更少,不能更多)的要求:

类型

要求

示例

阐明

明码

不输入


登录明码、领取明码等各种类型的明码

信用卡 CVV2

不输入


信用卡有效期

不输入


验证码

不输入


图形验证码、短信验证码、邮件验证码等

密钥、盐

不输入


用于加解密算法的密钥,音讯摘要的盐,以及数字签名及签名验证算法所应用的公私钥对等

会话 ID 设施指纹 (ID) 指纹 token 密文数据

前 5 后 5

7SuA8*TtslB

次要有以下类型:1. 利用的会话标识,比方:Web、APP、H5 等用于辨认会话状态信息的标识 2. APP 标识设施的设施指纹或者设施 ID3. APP 用于指纹验证的 token4. 密文数据指的是加密后的数据被掩码的字符无论多少位都输入 3 个 *

银行卡卡号

前 6 后 4

622666** 0831

银行卡卡号最多 19 位数字

手机号

前 3 后 4

137** 9574

定长 11 位数字

身份证号

前 1 后 1

3** X

定长 18 位

姓名

隐姓

* 世仁

将姓氏暗藏

IP 地址

前 1 后 1

.

.27

暗藏 IP 地址的第 2、第 3 段

邮箱地址

前 1 后 1

w**3@gmail.com

仅对 @ 之前的邮箱名称进行掩码,掩码局部不论多少位均输入 *

地址

隐号码

上海市西藏北路 *

上述仅列取出局部数据的显示要求,其余的显示准则为通过掩码后的数据无奈得悉原始数据。

实现了如上掩码的工具类,参考:https://github.com/frankiegao…

异样堆栈

异样堆栈个别会呈现在 ERROR 或者 WARN 级别的日志中,异样堆栈含有办法调用链的零碎,以及异样产生的本源。

异样堆栈的日志属于上一行日志的,在日志收集时须要将其划至上一行中。

日志文件

日志文件搁置于固定的目录中,依照肯定的模板进行命名,举荐的日志文件名称:

  • 以后正在写入的日志文件名:< 利用名 >[-< 性能名 >].log
  • 曾经滚入历史的日志文件名:< 利用名 >[-< 性能名 >].log.

日志配置

输入

依据不同的环境配置不同的日志输入形式:

  • 本地调试能够将日志输入到管制台上
  • 测试环境或者生产环境输入到文件中,每天产生一个文件,如果日志量宏大能够每个小时产生一个日志文件
  • 生产环境中的文件输入,能够思考应用异步文件输入,该种形式日志并不会马上刷新到文件中去,会产生日志延时,在进行利用时可能会导致一些还在内存中的日志未能及时刷新到文件中去而产生失落,如果对于利用的要求并不是十分高的话,可暂不思考异步日志

logback 日志工具能够在日志文件滚动后将前一文件进行压缩,以缩小磁盘空间占用,若应用 logback 对于日志量宏大的利用倡议开启该性能。

参考:《2020 最新 Java 根底精讲视频教程和学习路线!》

链接:https://juejin.cn/post/692409…

正文完
 0