工作总结!日志打印的11条建议

news2024/11/17 8:48:57

前言

大家好,我是 JavaPub。日志是我们定位问题的得力助手,也是我们团队间协作沟通(甩锅)、明确责任归属(撕B)的利器。没有日志的程序运行起来就如同脱缰的野🐎。打印日志非常重要。今天我们来聊聊日志打印的 N 个好建议~

image-20240314165434764

文章目录

    • 前言
      • 选择合适的日志等级
      • 要打印函数的入参、出参
      • 打印日志对象要做判空处理,避免阻断流程
      • 不要使用日志系统的(Log4j、Logback),要使用 Slf4j
      • 对低级别的日志输出,必须进行日志级别开关判断
      • 不要用e.printStackTrace()打印日志
      • 打印全部的异常信息,方便定位问题
      • 不要打印重复日志
      • 日志尽量使用英文
      • 核心业务逻辑,在每个分支首行都打印日志
      • 不要打印无意义的日志(不携带上下文、日志链路 id)


选择合适的日志等级

在开发中我们有常见的四种日志打印等级,debug、info、warn、error,要选择合适的等级打印,不要上来直接 info。

image-20240314214602647

  • error: 错误日志,指比较严重的问题,会对系统和有业务造成伤害。运维监控重点关注

  • warn: 警告日志,不会对系统运行造成大的影响,一般由开发人员关注

  • info: 关键日志,为了保留系统运行关键指标,比如函数的入参、出参,时间等信息。

  • debug: 开发日志,在开发调试阶段,记录对象数据在关键处理步骤中的变化情况、快速定位。


要打印函数的入参、出参

记录日志并不是要把所有信息都记录下来,那日志存储就要大到上天。我们只记录关键有效的日志,有效日志才是 battle 🆚 时杀手锏。

image-20240314214637143

哪些算是有效日志?比如函数的入口处,打印入参,还包括用户唯一标识 (uid)、链路标识 (traceId) 等。函数出口打印返回值及时间等。

    public String GetName(Request req, Integer id){
        log.debug("method start param: {}", req.UserID);
        
        String name = "JavaPub";
        log.debug("method end result: {}", name);
        return name;
    }

打印日志对象要做判空处理,避免阻断流程

image-20240314214712731

为了打印一行日志,程序写挂了。空指针异常在任何代码中都是最常见的异常之一。

反例:当 book 对象是 NULL 的话,这行日志就会抛空指针异常。

public void doSome(Book book){
    log.info("do do and print log: {}". book.getName());
    // do something...
    ...
}

不要使用日志系统的(Log4j、Logback),要使用 Slf4j

image-20240314214745191

Slf4j 是使用门面模式的日志框架,可以解耦具体的日志实现。可以在不修改代码的情况下,更换底层的日志框架

正例:

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(JavaPub.class);

对低级别的日志输出,必须进行日志级别开关判断

image-20240314214801114

对于 trace、debug、info 这些比较低的日志级别,必须进行日志级别开关。

正例:

开关判断逻辑通常放在日志工具类中。

public void doSomething(){
    User user = new User(1, "技术自媒体", "JavaPub");
    if (logger.isDebugEnabled()) {
        logger.debug("print debug log. 666 is {}", user.getName());
    }
}

反例:

public void doSth(){
    String name = "JavaPub";
    logger.trace("print debug log" + name);
    logger.debug("print debug log" + name);
    logger.info("print info log" + name);
    // 业务逻辑
    ...
}

当日志级别是 warn 时,以上日志不会打印,但是会执行字符串拼接操作,如果打印值是对象的话,还会执行 toString() 方法,浪费了系统资源,因此建议加上日志开关判断


不要用e.printStackTrace()打印日志

反例:

public void doSomething(){
    try{
        // 业务代码
        ...
    } catch (Exception e){
        e.printStackTrace();
    }
}
  • e.printStackTrace() 打印出的日志包含堆栈信息,导致我们的日志信息不规整、增加定位问题的难度。如果使用 ELK 分析日志也会非常困难。

  • e.printStackTrace() 语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,系统请求也将被阻塞。

正例:

public void doSomething(){
    try{
        // 业务逻辑
        ...
    } catch (Exception e){
        log.error("程序异常 failed", e);
    }
}

打印全部的异常信息,方便定位问题

image-20240314214823572

反例:

没有打印系统异常 e,无法定位出现了什么类型的异常。

public void doSth(){
    try{
        // 业务逻辑
        ...
    } catch (Exception e){
        log.error("发生了一个异常");
    }
}

不要打印重复日志

image-20240314214836800

在嵌套逻辑代码中打印重复日志,增加系统资源消耗占用。

反例:

public void doSomething(String s){
    log.info("do something and print log: {}", s);
    doSubSomething(s);
}private void doSubSomething(String s){
    log.info("do sub something and print log: {}", s);
    // 写点业务逻辑
    ...
}

正例:

应该直接删掉或者将为 debug 日志级别。


日志尽量使用英文

反例:

image-20240314155834611

建议:尽量在打印时日志时输出英文,防止中文编码与终端不一致导致打印出现乱码,对排查故障造成感染。


核心业务逻辑,在每个分支首行都打印日志

在编写核心业务逻辑代码时,遇到 if…else 或者 switch 这样的分支条件,在行首打印日志,通过日志可以快速排查定位异常。

public void doSomething(){
    if(user.isVip()){
        log.info("该用户是 JavaPub 会员,Id:{},开始处理会员逻辑",user,getUserId());
        // TODO 会员逻辑
    }else{
        log.info("该用户是非会员,Id:{},开始处理非会员逻辑",user,getUserId())
        // TODO 非会员逻辑
    }
}

不要打印无意义的日志(不携带上下文、日志链路 id)

image-20240314214848607

反例:

不携带任何业务信息的日志,对故障排查意义不大。

public void doSomething(){
    log.info("do something and print log. i am NB");
    // TODO 业务逻辑
    ...
}

正例:

  • 日志一定要携带业务信息相关内容,有利于快速定位问题原因
public void doSomething(Request req, User user){
    log.info("do something and print log, id={}, trace_id={}", user.GetId, req.GetTraceId);
    // TODO 业务逻辑
    ...
}

如何打印日志呢?总的来说不要让你的程序在黑盒总运行,打印关键信息、保证在出现异常时通过日志快速定位到那里就可以啦。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1522050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开发指南005-前端配置文件

平台要求无论前端还是后端,修改配置可以直接用记事本修改,无需重新打包或修改压缩包里文件。就前端而言,很多系统修改配置是在代码里修改,然后打包或者是修改编译环境来重新编译。 平台前端的配置文件为/static/js/下qlm_config.j…

家电工厂5G智能制造数字孪生可视化平台,推进家电工业数字化转型

家电5G智能制造工厂数字孪生可视化平台,推进家电工业数字化转型。随着科技的飞速发展,家电行业正迎来一场前所未有的数字化转型。在这场制造业数字化转型中,家电5G智能制造工厂数字孪生可视化平台扮演着至关重要的角色。本文将从数字孪生技术…

SwinTransformer论文笔记

What:Swin Transformer 用了移动窗口的层级式的Vision Transformer. Swin Transformer的思想是让Vision Transformer也能像CNN一样也能分成几个block,也能做层级式的特征提取,从而让提取的特征具有多尺度的概念。(设计的初衷是作为…

计算机二级Python题目13

目录 1. 基本题 1.1 基本题1 1.2 基本题2 1.3 基本题3 2. turtle画图 3. 大题 3.1 大题1 3.2 大题2 1. 基本题 1.1 基本题1 lseval(input()) s"" for item in ls:if type(item)type("香山"):s item print(s) 1.2 基本题2 import random random.se…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的手写数字和符号识别(深度学习训练+UI界面+训练数据集)

摘要:开发手写数字和符号识别对于智能交互系统具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个手写数字和符号识别,并提供了完整的实现代码。该系统基于强大的YOLOv8算法,并对比了YOLOv7、YOLOv6、YOLOv5,展示了不同模…

电源常用通讯电路详解

数字电源的采样和PWM驱动电路原理,通过这些技术,数字电源可以在内部形成控制闭环。但是要实现电源的控制和管理,还是需要与数字控制核心建立通讯连接。本期将带领大家了解数字电源常用的通讯电路。 一、常用的通讯方式 在前面数字电源与模拟…

运行gazebo机器人模型没有cmd_vel话题

运行赵虚左教程代码出现上诉问题 roslaunch urdf02_gazebo demo03_env.launch 原因:缺少某个包 在工作空间catkin_make编译发现报错 解决: sudo apt-get install ros-noetic-gazebo-ros-pkgs ros-noetic-gazebo-ros-control 下载后再次运行launch文件…

redis中List和hash数据类型

list类型是用来存储多个有序的字符串的,列表当中的每一个字符看做一个元素,一个列表当中可以存储一个或者多个元素,redis的list支持存储2^32-1个元素。redis可以从列表的两端进行插入(pubsh)和弹出(pop&…

Flink 集群部署模式

文章目录 前言一、会话模式(Session Mode)二、单作业模式(Per-Job Mode)三、应用模式(Application Mode) 前言 Flink支持多种集群部署模式,以满足不同场景和需求。以下是Flink的主要集群部署模…

外包干了3天,技术明显进步。。。。。

先说一下自己的情况,本科生,19年通过校招进入南京某软件公司,干了接近2年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…

奇怪的比赛(Python,递归,状态压缩动态规划dp)

目录 前言:题目:思路:递归:代码及详细注释: 状态压缩dp:代码及详细注释: 总结: 前言: 这道题原本是蓝桥上的题,现在搜不到了,网上关于此题的讲解…

【ESP32接入国产大模型之MiniMax】

1. MiniMax 讲解视频: ESP32接入语言大模型之MiniMax MM智能助理是一款由MiniMax自研的,没有调用其他产品的接口的大型语言模型。MiniMax是一家中国科技公司,一直致力于进行大模型相关的研究。 随着人工智能技术的不断发展,自然语…

springboot绩效管理系统(源码私信呢)

链接如下: 20240316_173655_哔哩哔哩_bilibili 代码解析理解: 前置知识:三层架构: con...>ser接口>imp接口实现类>mapper写sql语句Controller 层控制层-->调用业务方法来控制业务逻辑 ,功能的请求和响应控制,controller层负责前…

减肥实践和经验分享

在当下竞争激烈、物质丰富的现代社会,每个人都会同时面临两个不同指向的问题 和别人竞争,实现个人价值,创造个人财富,此为:显性指向(explicit)和自己比拼,实现个人内在提升&#xf…

数据库——书籍+内容0.1版本

背景:将一本书,存入我们的数据库中,并可以查出来 采用:第三范式(3NF)设计模式 设计数据库模板 第一范式(1NF):确保表的每一列都是不可分割的原子数据项。 第二范式&…

什么时候去检测大数据信用风险比较合适?

什么时候去检测大数据信用风险比较合适?在当今这个数据驱动的时代,大数据信用风险检测已经成为个人的一项重要需求。本文将从贷前检测、信息泄露检测和定期检测三个方面,阐述何时进行大数据信用风险检测较为合适。 一、贷前检测 大数据信用风险检测在贷…

1.Spring入门

1.1 Spring简介 Spring是一个轻量级Java 企业级应用程序开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。 Spring Fra…

力扣701. 二叉搜索树中的插入操作

思路:往二叉搜索树中插入一个值,树的结构有多种符合的情况,那我们可以选一种最容易的插入方式,反正只需要插入一个值而已,我们不难发现,不管插入什么值,都可以安排插入到叶子节点上。 再利用二叉…

Internet协议的安全性

Internet协议的安全性 文章目录 Internet协议的安全性1. 网络层1. IP*62. ARP*33. ICMP * 3 2. 传输层协议1. TCP1. * SYN-Flood攻击攻击检测* 防御 2. TCP序号攻击攻击 3. 拥塞机制攻击 2. UDP 3. 应用层协议1. DNS攻击*3防范*3: 2. FTP3. TELNET: 改用ssh4. 电子邮件1. 攻击2…

Microsoft Visio 编辑属性值

Microsoft Visio 编辑属性值 1. 编辑属性值References 1. 编辑属性值 单击长度或高度位置,弹出形状的各属性值,点击编辑对应的属性值。 ​ References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/