首页 > 其他分享 >一次logging日志框架的分享

一次logging日志框架的分享

时间:2022-12-23 16:57:40浏览次数:56  
标签:logging log4j user debug Logging 日志 分享 logger

目前市面上有很多日志框架,不同的项目依赖不同的框架,到最后我们的项目里就会有很多日志框架

历史

先来梳理一下市面上的日志框架。

  • log4j 日志实现

    1996年推出,Apache 曾经建议SUN把log4j收入JDK中。

  • JUL(Java Util Logging) 日志实现

    2002年由SUN推出,想要跟log4j比一比

  • JCL(Jakarta Commons Logging)日志门面

    2002年Apache推出,为日志框架提供了统一的接口。

  • SLF4J(Simple Logging Facade for Java)日志门面

    2005年log4j的原作者推出,因为出的晚,为了兼容目前市面上所有的日志实现,顺便推出了各种桥接包

  • logback 日志实现

    log4j的原作者推出

  • log4j2 日志门面(log4j2-api)+实现(log4j2-core)

    Apache 推出的logback的竞品,具有logback的全部功能。

  • jboss-logging 日志门面

    redhat推出的,能自动检测类路径下的日志实现,自动适配。

    https://docs.jboss.org/hibernate/orm/current/topical/html_single/logging/Logging.html

关系梳理

log-slf4j框架关系图

log-log4j框架关系图

日志依赖最佳实践

  1. 保证项目里只有一个日志门面和实现

    项目中有多个实现的话,不仅配置麻烦(每个实现都有不一样的配置文件),而且使用起来混乱,可能出现不一样的接口使用不同的日志框架的问题。

  2. 将日志实现的依赖设置为Optionalruntime scope;日志门面设置为Optional

    我们项目中有那么多日志依赖就是因为没有设置为Optional,导致的依赖传递。

    设置成runtime后,写代码的时候就不能直接调用日志实现的API了,避免直接调用。

LOG4J2介绍

异步性能强大

Peak throughput comparison

零 GC(Garbage-free)

不会导致 GC。各种 Message 对象,字符串数组,字节数组全部复用不重复创建。

更高性能 I/O 写入的支持

提供了一个 MemoryMappedFileAppender,I/O 部分使用 MemoryMappedFile 来实现,可以得到很高的 I/O 性能。

内存映射文件是一种允许Java程序直接从内存访问的特殊文件。通过将整个文件或者文件的一部分映射到内存中、操作系统负责获取页面请求和写入文件,应用程序就只需要处理内存数据,这样可以实现非常快速的IO操作。用于内存映射文件的内存在Java的堆空间以外。Java中的java.nio包支持内存映射文件,可以使用MappedByteBuffer来读写内存

使用 String.format 的形式格式化参数

log4j-api除了提供了slf4j提供的支持{}参数占位符的接口外,还提供了 String.format 的形式:

logger.debug("Logging in user %s with birthday %s", user.getName(), user.getBirthdayCalendar());
logger.debug("Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());
logger.debug("Integer.MAX_VALUE = %,d", Integer.MAX_VALUE);
logger.debug("Long.MAX_VALUE = %,d", Long.MAX_VALUE);

不过这些接口属于log4j-api,需要使用log4j-api做门面。同时还需要使用LogManager.getFormatterLogger来获取logger,而不是LogManager.getLogger

不过普通的logger中也提供了一个printf()方法,实现上面的效果:

logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());

“惰性”打日志(lazy logging)

为了在低日志级别下不浪费系统资源,一般都需要:

if(logger.isDebugEnabled()){
  logger.debug("入参报文:{}",JSON.toJSONString(policyDTO));
}

log4j2提供了在低日志级别下更优雅的输出方式:

//debug(String message, Supplier<?>... paramSuppliers);
logger.debug("入参报文:{}",() -> JSON.toJSONString(policyDTO));

slf4j在2.0.0也提供了类似的方法:

//fluent API
logger.atDebug().addArgument(() -> t16()).log(msg, "Temperature set to {}. Old temperature was {}.", oldT);

日志打印的几个建议

  1. 选择合适的日志级别

    • fatal:致命错误,会影响服务运行状态的错误。一般用不到。

    • error:影响到程序、当前请求正常运行的异常情况。

      如果捕获了异常需要记录完整的错误堆栈;

      如果抛出了异常就不要记录error日志,由调用方处理。

    • warn:不应该出现但是不影响程序、当前请求正常运行的异常情况。

    • info:系统运行信息,记录排查问题的关键信息,如调用时间、出参入参、业务逻辑的变化等等;

    • debug:用于开发DEBUG的,关键逻辑里面的运行时数据;

    • trace:最详细的信息,基本不用。

  2. 记录方法的入参和出参

  3. 选择合适的日志格式

  4. 在条件分支的各个分支都打印日志

  5. 日志级别比较低时,进行日志开关判断

    ![image-20220120101047576](/Users/machao27/Library/Application Support/typora-user-images/image-20220120101047576.png)

    log4j2有支持lambda表达式的懒加载模式:

    if (logger.isTraceEnabled()) {
     logger.trace("Some long-running operation returned {}", expensiveOperation());
    }
    

    可以改为:

    logger.trace("Some long-running operation returned {}", () -> expensiveOperation());
    

    slf4j目前不支持。

  6. 不能直接使用日志实现中的 API,而是使用日志门面中的API。

    image-20220120100955132

  7. 建议使用参数占位{},而不是用+拼接

    image-20220120100504601

    log4j2支持跟String.format()一样的格式化方式:

    logger.debug("Integer.MAX_VALUE = %,d", Integer.MAX_VALUE);
    

    Formatting_Parameters

  8. 用[]来分隔日志输出中的参数

    log.info("获取用户[{}]的用户信息时出错",userName);
    
  9. 使用异步的方式来输出日志

    为什么?因为打印日志是将日志输出到文件,受限于硬盘的性能,输出到文件是很浪费时间的

    同步阻塞IO(Blocking IO)
    同步阻塞IO

    同步非阻塞NIO(None Blocking IO)

    同步非阻塞NIO

    IO多路复用模型(I/O multiplexing)

    IO多路复用模型

    异步IO模型(asynchronous IO)

    异步IO模型

  10. 记录异常的时候要同时输出所有错误信息。

    try {
     //业务逻辑
    } catch (Exception e) {
     logger.error('你的程序有异常啦',e);
    }
    
  11. 日志文件分离,使用RollingFileAppender

  12. 不要重复打印日志

    image-20220120101141134

  13. 不要使用JSON序列化

    image-20220120101306364

标签:logging,log4j,user,debug,Logging,日志,分享,logger
From: https://www.cnblogs.com/macho8080/p/17001043.html

相关文章

  • 深入理解Kafka核心设计-日志存储
    一、文件系统中存储方式1.1树形结构图1.2目录结构【分而治之】一个topic有多个分区,一个分区就是一个Log(文件夹),文件夹命名方式:<topic>-<partition>如创建订单topic:CRE......
  • 饮食分享平台
        系统的实体类  项目的启动类packagecom.example.entity;importjavax.persistence.*;publicclassAccount{@Id@GeneratedValue(strategy=......
  • 开发日志
    作为个小白,开发过程中总会遇到一些困扰,也会有许多小惊喜,此日志作为一个笔记文档,随缘记录使用一个list接收一个字符串表示的列表,以","分隔statusList=newArrayList(S......
  • 【视频】R语言逻辑回归(Logistic回归)模型分类预测病人冠心病风险|数据分享|附代码数据
    原文链接:http://tecdat.cn/?p=22410 最近我们被客户要求撰写关于逻辑回归的研究报告,包括一些图形和统计输出。本文介绍了逻辑回归并在R语言中用逻辑回归(Logistic回归)模......
  • 【视频】R语言生存分析原理与晚期肺癌患者分析案例|数据分享|附代码数据
    原文链接:http://tecdat.cn/?p=10278最近我们被客户要求撰写关于生存分析的研究报告,包括一些图形和统计输出。生存分析(也称为工程中的可靠性分析)的目标是在协变量和事件时......
  • MySQL日志
    1.错误日志错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正......
  • 分享巧记Linux命令的方法
    最近有些学弟经常私信问我说,他们自己是Linux方面的小白,对于Linux的命令了解十分的少,虽然每次跟着我推荐的学习视频教程可以进行操作,但是离开了视频,就又是两眼抓瞎,什么也想......
  • C語言日志
    怎麽樣把函數寫在數值後滿仍可以運行intAdd(intx,inty);//聲明一下intmain(){inta=10;intb=20;intsum=20;sum=Add(a,b);printf("%d\n",sum);......
  • 全国中文纠错大赛达观冠军方案分享:多模型结合的等长拼写纠错
    参与任务 中文拼写检查任务是中文自然语言处理中非常具有代表性和挑战性的任务,其本质是找出文本段落中的错别字。这项任务在各种领域,如公文,新闻、财报中都有很好的落地应用......
  • 中文语法纠错全国大赛获奖分享:基于多轮机制的中文语法纠错
    中文语法纠错任务旨在对文本中存在的拼写、语法等错误进行自动检测和纠正,是自然语言处理领域一项重要的任务。同时该任务在公文、新闻和教育等领域都有着落地的应用价值。但......