【Java日志】你真的知道怎么使用Java日志API吗?

2023-11-18

一、背景

  在平时的开发过程中,常常看到一些编码不规范的打印日志的代码,这些代码虽然使用了日志API,却达不到预期的记录日志的目的,这正是我写本篇文章的背景原因。对于一些经验不足的开发人员来讲,他们可能会照猫画虎或者直接就使用日志API,只要代码不报错就行,往往不去理解API的含义,也没有系统学习底层原理。其实,我也是这样过来的。下面就从以下两个方面分享一些错误使用Java日志API的示例,一起学习避坑。

  • 占位符的错误使用
  • 异常日志的错误使用

二、Java日志API错误使用示例

1、占位符的错误使用示例

  • 示例一:使用了占位符,却不给占位参数
// 使用了占位符,却对参数进行字符串拼接
Integer userId = 1001;
Long merchantId = 1000001234L;
// 输出结果为:创建订单请求参数:userId={}, merchantId={}10011000001234
log.info("创建订单请求参数:userId={}, merchantId={}" + userId + merchantId);
// 输出结果为:创建订单请求参数:userId={}1001, merchantId={}1000001234
log.info("创建订单请求参数:userId={}" + userId + ", merchantId={}" + merchantId);

// 使用了占位符,忘记给占位参数
// 输出结果即为原样输出
log.info("创建订单请求参数:userId={}, merchantId={}");
log.info("创建订单请求结果:orderId={}")

错误原因:以上代码实际使用到的是

/**
 * Log a message at the INFO level.
 *
 * @param msg the message string to be logged
 */
public void info(String msg);

而不是

/**
 * Log a message at the INFO level according to the specified format
 * and arguments.
 * <p/>
 * <p>This form avoids superfluous string concatenation when the logger
 * is disabled for the INFO level. However, this variant incurs the hidden
 * (and relatively small) cost of creating an <code>Object[]</code> before invoking the method,
 * even if this logger is disabled for INFO. The variants taking
 * {@link #info(String, Object) one} and {@link #info(String, Object, Object) two}
 * arguments exist solely in order to avoid this hidden cost.</p>
 *
 * @param format    the format string
 * @param arguments a list of 3 or more arguments
 */
public void info(String format, Object... arguments);
  • 示例二:不使用占位符,却给占位参数
Integer userId = 1001;
Long merchantId = 1000001234L;
// 输出结果为:创建订单请求参数:userId=, merchantId=
// More arguments provided (2) than placeholders specified (0) 
log.info("创建订单请求参数:userId=, merchantId=", userId, merchantId);

// 输出结果为:创建订单请求参数:userId=1001, merchantId=
// More arguments provided (2) than placeholders specified (1) 
log.info("创建订单请求参数:userId={}, merchantId=", userId, merchantId);

不给占位符或占位符数量少于占位参数数量,这种情况多余的占位参数会不输出。

  • 示例三:使用了占位符,占位参数数量与占位符数量不一致
Integer userId = 1001;
Long merchantId = 1000001234L;
// 输出结果为:创建订单请求参数:userId=1001, merchantId={}
// Fewer arguments provided (1) than placeholders specified (2)  
log.info("创建订单请求参数:userId={}, merchantId={}", userId);

Long orderId = 100110000004321L;
// 输出结果为:创建订单请求结果:userId=1001, merchantId=1000001234
// More arguments provided (3) than placeholders specified (2) 
log.info("创建订单请求结果:userId={}, merchantId={}", userId, merchantId, orderId);

占位参数比占位符少,则多余的占位符不被替换,原样输出;
占位参数比占位符多,则多余的占位参数不被输出。

  • 正确示例:使用占位符,且占位参数数量与占位符数量保持一致
Integer userId = 1001;
Long merchantId = 1000001234L;
// 输出结果为:创建订单请求参数:userId=1001, merchantId=1000001234
log.info("创建订单请求参数:userId={}, merchantId={}", userId, merchantId);

Long orderId = 100110000004321L;
// 输出结果为:创建订单请求结果:userId=1001, merchantId=1000001234, orderId=100110000004321
log.info("创建订单请求结果:userId={}, merchantId={}, orderId={}", userId, merchantId, orderId);

2、异常日志的错误使用示例

  • 示例一:仅输出异常信息,不输出异常堆栈
try {
   Long orderId = createOrder(userId, merchantId);
   int num = 666 / 0;
} catch (Exception e) {
    // 输出结果为:创建订单失败:user=1001, msg=/ by zero
    log.error("创建订单失败:user={}, msg={}", userId, e.getMessage());
}

这里不是说只输出异常信息就是一定不对的,只是不输出异常堆栈信息不便于排查与定位问题,建议尽量避免只输出异常信息。我曾经犯过这种错误,生产环境因为数据问题导致空指针异常,而异常信息中只能看到null,比如“创建订单失败:msg=null”。

  • 示例二:输出异常堆栈,但却给占位符
try {
   Long orderId = createOrder(userId, merchantId);
   int num = 666 / 0;
} catch (Exception e) {
    /* 输出结果为:
        12:35:35.984 [main] ERROR stu.ljxcolin.logging.slf4j.Slf4jTests - 创建订单失败:user=1001, e={}
        java.lang.ArithmeticException: / by zero
            at stu.ljxcolin.logging.slf4j.Slf4jTests.test01(Slf4jTests.java:28)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:498)
            at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) 
     */
    log.error("创建订单失败:user={}, e={}", userId, e);
}
  • 正确示例:输出异常堆栈信息,且throwable不需要给占位符
try {
   Long orderId = createOrder(userId, merchantId);
   int num = 666 / 0;
} catch (Exception e) {
    /* 输出结果为:
        12:35:35.984 [main] ERROR stu.ljxcolin.logging.slf4j.Slf4jTests - 创建订单失败:user=1001
        java.lang.ArithmeticException: / by zero
            at stu.ljxcolin.logging.slf4j.Slf4jTests.test01(Slf4jTests.java:28)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:498)
            at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)  
     */
    log.error("创建订单失败:user={}", userId, e);
}

// 请参见以下两个API
// public void error(String format, Object... arguments);
// public void error(String msg, Throwable t);

好了,Java日志API错误使用示例就分享完了。在我们使用Java日志API时需谨记:(1)使用占位符时要给占位参数,且占位符的数量要与占位参数的数量保持一致。(2)输出异常堆栈信息时,throwable不需要给占位符,把throwable放在最后一个参数。

三、推荐一个学习Java日志的项目

  当然,只学习了如何正确使用日志API是不够的,下面介绍下我在学习各种日志框架过程中建立的一个Git仓库logging。该项目主要是对日志实现和日志门面进行了较全面的学习,包括日志的API、日志等级、日志格式以及日志配置文件的学习,还使用SpringBoot整合了Logback和Log4j2。大家可以参照该项目进行进一步的学习,也可以给该仓库提出建议或提交代码。仓库地址如下:
https://www.gitee.com/ljxcolin/logging.git。

logging
loggingApplication

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Java日志】你真的知道怎么使用Java日志API吗? 的相关文章

随机推荐

  • 计算机视觉论文-2021-07-19

    本专栏是计算机视觉方向论文收集积累 时间 2021年7月19日 来源 paper digest 欢迎关注原创公众号 计算机视觉联盟 回复 西瓜书手推笔记 可获取我的机器学习纯手推笔记 直达笔记地址 机器学习手推笔记 GitHub地址 1 T
  • 在struts1.1框架下,利用smartupload实现文件的上传(可以是多个文件)

    1 前端页面upload jsp 后台处理程序UplodAction java 2 struts config的配置参数如下 没有设置 ActionForm
  • JAVA的WebService规范(支持)

    SOA Service Oriented Architecture 面向服务架构是一种思想 它将应用程序的不同功能单元通过中立的契约 独立于硬件平台 操作系统和编程语言 联系起来 使得各种形式的功能单元更好的集成 目前来说 WebServi
  • windows7旗舰版 appium环境搭建

    1 安装jdk 8u171 windows x64 exe 注意配置环境变量 参考资料 https jingyan baidu com article 6dad5075d1dc40a123e36ea3 html java version 查
  • Modbus RTU协议各知识点入门 + 实例

    文章目录 1 起因 2 几个重点 2 1 一些难懂的概念 2 2 CRC的高低位 2 3 其他 3 介绍 3 1 起源 3 2 分类 4 格式 4 1 串口协议 4 2 帧格式 5 数据类型 6 功能码 7 CRC16 modbus 8 实
  • React(Hook介绍)

    为什么要用Hook 介绍Hooks之前 首先要给大家说一下React的组件创建方式 一种是类组件 一种是纯函数组件 并且React团队希望 组件不要变成复杂的容器 最好只是数据流的管道 开发者根据需要 组合管道即可 也就是说组件的最佳写法应
  • 如何在 CentOS 8 上安装 Python 3.8

    本文来自于 阿里云官方镜像站 https developer aliyun com mirror utm content g 1000307095 原文链接 https developer aliyun com article 756221
  • 报错java.lang.Long cannot be cast to java.lang.Integer解析

    用博客记录工作中出现的问题 给自己一个提醒 也给其他朋友一些借鉴 报错 java lang Long cannot be cast to java lang Integer Long 无法转化成Integer类型 这个异常 经常出现在hin
  • 正确理解Widget::Widget(QWidget *parent) :QWidget(parent)这句话

    该如何理解下面段代码的第二行QWidget parent 1 Widget Widget QWidget parent 2 QWidget parent 3 4 在讲解原因之前 先请大家看下面的一个例子 include
  • git切换分支时报错(error: pathspec ‘master‘ did not match any file(s) known to git.)的解决方法

    git切换分支时报错 切换分支 root git my code git checkout master 产生如下报错 error pathspec master did not match any file s known to git
  • 用 Python 爬取网红城市大长沙!

    这两天获取了两份关于长沙的数据 长沙景点和长沙美食 之后进行了分析 如果有朋友想去长沙或者周边城市玩 要仔细看看喔 导入库 长沙景点 数据获取 长沙景点的数据获取方法和之前那篇关于厦门的文章是一样的 只是重新跑了一遍代码 具体过程不再阐述
  • HADOOP集群搭建

    安装步骤 机器mini yum mini2 mini3 mini4 注意 下面的步骤在4台机子上都要做的操作 1先将虚拟机的网络模式选为NAT 2修改主机名 vi etc sysconfig network NETWORKING yes H
  • git:kex_exchange_identification:Connection closed by 52.74.223.119 port 22

    使用Rider的git进行push操作时提示 kex exchange identification Connection closed by remote host Connection closed by 52 74 223 119 p
  • AJAX 缓存处理

    关于AJAX请求服务器后缓存数据 造成没有及时刷新的问题 最近在做项目的时候 使用了ajax去请求服务器的数据 刚开始还可以 我测试一切运行正常 我不是专业的测试人员哈 所以还是有些问题没有测出来哈 后来ajax请求的数据变化了 但是页面数
  • 设计模式之命令模式

    在日常生活中 我们常常会遇到这样一些问题 需要向某些对象发送请求 但是并不知道请求的接收者是谁 也不知道被请求的操作是哪个 我们只需在程序运行时指定具体的请求接收者即可 此时 可以使用命令模式来进行设计 使得请求发送者与请求接收者消除彼此之
  • react 显示当前时间_react中monent如何获取日期?

    方法 1 使用 npm install moment save 安装moment 2 在组件中使用import语句引入moment 3 使用monent提供的方法来获取日期 例 moment format 获取当前时间 本教程操作环境 wi
  • OneFlow 中的 Softmax

    Softmax 是深度学习模型中的常见算子 PyTorch 的 Softmax 算子直接调用 cuDNN 的接口 而 OneFlow 内部针对输入数据的类别数量 采用3个 kernel 来分别处理 在多数情况下都可以获得比 cuDNN 更优
  • 【Git】(三)回退版本

    1 git reset命令 1 1 回退至上一个版本 git reset hard HEAD 1 2 将本地的状态回退到和远程的一样 git reset hard origin master 注意 谨慎使用 hard 参数 它会删除回退点之
  • redis必杀高级:安全

    题记 我们可以通过 redis 的配置文件设置密码参数 这样客户端连接到 redis 服务就需要密码验证 这样可以让你的 redis 服务更安全 例如 设置密码 查看是否设置了密码验证 127 0 0 1 6379 gt CONFIG ge
  • 【Java日志】你真的知道怎么使用Java日志API吗?

    你真的知道怎么使用Java日志API吗 一 背景 二 Java日志API错误使用示例 1 占位符的错误使用示例 2 异常日志的错误使用示例 三 推荐一个学习Java日志的项目 一 背景 在平时的开发过程中 常常看到一些编码不规范的打印日志的