在Java代码中打日志需要注意什么?

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 为什么要打日志? 日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手! 做一件事情之前,先思考为什么。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


为什么要打日志?

日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手!

做一件事情之前,先思考为什么。为什么我们在开发中,需要打日志?原因很简单,没人能保证自己写的程序没有BUG,即使你做了足够的测试,也只是能降低产生BUG的概率而已。

尤其是当今分布式环境,定位问题变得越来越复杂。所以我们想要获取一些程序“运行时”的信息,日志就是最方便的。

所以,这种福泽后来人的好东西,当然要用起来了~

Java日志框架

要说Java日志框架啊,要从远古时代的JDK 1.3之前说起。那时候大家打印日志就是直接输出到STDOUT或者STDERR流。

System.out.println()
System.err.println()
e.printStackTrace()

于是log4j在大牛Ceki中应运而生,后面经过一系列的发展,以及Ceki与Apache的吃瓜事件,逐渐发展为slf4j、logback、log4j2三种最主流的日志框架。

  • slf4j: 日志的“门面”框架,对于用户来说只要使用SLF4J提供的接口,即可隐藏日志的具体实现。
  • logback: 与slf4j一样,为Ceki大神创建,所以原生实现slf4j,Spring Boot钦点的默认日志框架。
  • log4j2: Apache顶级项目,性能优于logback,尤其是异步输出,表现比较好。

1

所以现在主流的框架一般是slf4j + logback或者slf4j + log4j2。

2

如何选择日志级别?

关于日志级别,不同的标准有不同的定义。鉴于slf4j差不多已经一统天下,所以我们主要介绍slf4j定义的5种日志级别。

3

5种日志级别

ERROR、WARN、INFO、DEBUG、TRACE这五个级别从高到低,「配置级别越高日志输出就越少」。

我们在配置输出某个级别的日志的时候,它也会输出比它高级别的日志。比如我们配置日志在INFO这一级别,它会输出ERROR、WARN、INFO三种级别的日志。

顾名思义,这几种级别的日志分别会输出不同程度信息:

  • ERROR:错误日志,比较严重的错误,对正常业务有影响;
  • WARN:警告日志,一般的错误,对业务影响不大;
  • INFO:信息日志,记录一些日常的东西,比如调用时间、出参入参、业务信息等;
  • DEBUG:用于DEBUG的,关键逻辑里面的运行时数据;
  • TRACE:最详细的信息,一般这些信息只记录到日志文件中。

4

级别继承

有时候,我们可能根据不同的业务输出不同级别的日志。比如某些重要业务输出INFO级别,其他业务输出WARN级别的日志,同时关闭所有库、框架的日志,比如Spring等。

5

日志级别继承

一般情况,我们设置root级别了就行了,对于某些业务有特殊要求的话,对特定的包配置就行了。这里值得一提的是,谨慎开启低级别的日志,尤其是对root级别。之前听说过一个故障,就是为了Debug一个问题,在生产环境开启了DEBUG日志,并且是root级别,导致瞬间打印出巨量的日志,磁盘撑不住,造成了生产事故。

1

注意事项

开关判断

在阿里的Java开发手册中,有这么一条规约:

2

也就是说,在日志级别比较低的时候,应该在打日志前增加一个判断,「减少不必要的方法调用开销」。举个例子:

3

上面这段代码,我在打debug日志的时候,调用了user.getId()方法,这个时候,即使我们配置只打INFO级别的日志,运行到这段代码的时候,仍然会调用user.getId()方法,造成不必要的开销。

如果我们在前面添加一个判断就可以解决这个问题:

4

当然,这个根据自己的项目来。「如果你的项目部署肯定会开启INFO级别的日志,那INFO级别的日志,可以不加条件判断」。

使用参数占位

在上面的例子中,我们使用了大括号{}来作为日志中的占位符。相比于使用+操作符进行字符串的拼接,使用占位符可以让我们的代码更加优雅简洁。

除此以外,String字符串的拼接会使用StringBuilder的append()方式,有一定的性能损耗。使用占位符仅是替换动作,可以有效提升性能。

日志越多越好?

日志其实也是代码中的一部分。我们都知道,代码的可读性有多重要。其实并不需要每个地方都打上日志,这样我们在分析日志的时候也比较难快速定位关键信息。

我们要明白的一点是,「我们需要的不是日志,而是有效日志」。更何况,无效日志打多了,费磁盘~

只需要在我们关注的地方,打印关键的信息就可以了。比较常用的是我们通常能够快速定位数据的唯一id,比如gid等。

同步 vs 异步

我们知道,日志最终会输出到文件或者其它输出流,会重度使用IO。而异步可以显著提升IO性能,所以如果不是有特殊的需求,通常的建议使用异步的方式来输出日志。

以logback为例,要配置异步很简单,使用AsyncAppender就行:

4

trace Id

如今的系统做得越来越大,越来越复杂。前后端分离、分布式架构使得排查问题、追踪调用链路也变得越来越复杂。为了解决这个问题,我们可以使用一个trace Id来标识一次完整的调用链路。

目前主流的日志框架已经有这方面的支持了。拿logback为例,它提供了一种MDC机制,MDC为“Mapped Diagnostic Context”(映射诊断上下文),具体实现就是org.sl4j.MDC这个类。本文就不详细介绍如何使用它的了,感兴趣的同学可以去参考官方文档。

日志文件分离

我们打印日志是为了获取一些我们需要的信息。所以我们可以把不同类型的日志分离出去,比如access_log,或者ERROR级别的log,都可以单独打印到一个文件里。

也可以根据不同的业务模块,打印到不同的日志文件里,这样我们排查问题和做数据统计的时候就会比较方便。

获取日志实例

使用过日志框架的同学,对或多或少在类里定义过日志实例吧。但根据笔者的观察,大家定义日志的姿势各有千秋。有叫log的,有叫logger的,也有叫LOGGER的。定义的时候也都比较随意,比如不加static,不加final,获取实例的时候有传入this的,也有传入.class的。

这里不得不提一个优秀的Lombok注解:@Slf4j。它能够根据你的类自动注入一个log实例,非常方便快捷,如果你的项目使用了Lombok,不妨用起来。如果没有使用Lombok也没有关系,可以参考它的定义方式:

5

以上,大家就可以愉快地打日志啦~

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-05-05
本文作者:编了个程
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
20天前
|
Kubernetes jenkins 持续交付
从代码到k8s部署应有尽有系列-java源码之String详解
本文详细介绍了一个基于 `gitlab + jenkins + harbor + k8s` 的自动化部署环境搭建流程。其中,`gitlab` 用于代码托管和 CI,`jenkins` 负责 CD 发布,`harbor` 作为镜像仓库,而 `k8s` 则用于运行服务。文章具体介绍了每项工具的部署步骤,并提供了详细的配置信息和示例代码。此外,还特别指出中间件(如 MySQL、Redis 等)应部署在 K8s 之外,以确保服务稳定性和独立性。通过本文,读者可以学习如何在本地环境中搭建一套完整的自动化部署系统。
51 0
|
2天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
2天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
15 4
|
2天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
6天前
|
SQL JavaScript 前端开发
基于Java访问Hive的JUnit5测试代码实现
根据《用Java、Python来开发Hive应用》一文,建立了使用Java、来开发Hive应用的方法,产生的代码如下
27 6
|
13天前
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
40 11
|
5天前
|
Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第14天】本文旨在揭示Java 8中引入的Lambda表达式如何革新了我们编写和管理代码的方式。通过简洁明了的语言和直观的代码示例,我们将一起走进Lambda表达式的世界,了解其基本概念、语法结构以及在实际编程中的应用。文章不仅会展示Lambda表达式的魅力所在,还会指导读者如何在日常工作中有效利用这一特性,以提高编码效率和程序可读性。
|
11天前
|
并行计算 Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
Lambda表达式在Java 8中引入,旨在简化集合操作和并行计算。本文将通过浅显易懂的语言,带你了解Lambda表达式的基本概念、语法结构,并通过实例展示如何在Java项目中应用Lambda表达式来优化代码,提高开发效率。我们将一起探讨这一现代编程工具如何改变我们的Java编码方式,并思考它对程序设计哲学的影响。
|
11天前
|
安全 Java 测试技术
掌握Java的并发编程:解锁高效代码的秘密
在Java的世界里,并发编程就像是一场精妙的舞蹈,需要精准的步伐和和谐的节奏。本文将带你走进Java并发的世界,从基础概念到高级技巧,一步步揭示如何编写高效、稳定的并发代码。让我们一起探索线程池的奥秘、同步机制的智慧,以及避免常见陷阱的策略。
|
18天前
|
Java API 开发者
代码小妙招:用Java轻松获取List交集数据
在Java中获取两个 `List`的交集可以通过 `retainAll`方法和Java 8引入的流操作来实现。使用 `retainAll`方法更为直接,但会修改原始 `List`的内容。而使用流则提供了不修改原始 `List`、更为灵活的处理方式。开发者可以根据具体的需求和场景,选择最适合的方法来实现。了解和掌握这些方法,能够帮助开发者在实际开发中更高效地处理集合相关的问题。
14 1