• Java日志系列——概述,JUL


    日志

    概述

    日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
    在计算机中,日志文件是记录在操作系统或其他软件运行中发生的事件或在通信软件的不同用户之间的消息的文件。记录是保持日志的行为。在最简单的情况下,消息被写入单个日志文件。

    日志的作用

    1. 调试
    2. 错误定位
    3. 数据分析

    常用日志框架

    1. JUL
    2. logback
    3. log4j
    4. log4j2

    JUL

    JUL全称ava util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。

    组件

    1. Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。
    2. Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
    3. Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
    4. Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
    5. Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。

    级别(从高到低)

    1. severe
    2. warning
    3. info
    4. config
    5. fine
    6. finer
    7. finest

    QuickStart(控制台打印日志)

        @Test
        void contextLoads() {
            //获取logger对象
            final Logger test1 = Logger.getLogger("test1");
            //严重级别日志(最高级别)
            test1.severe("最高严重级别的日志输出");
            //警告级别日志
            test1.warning("警告类型的日志输出");
            //信息级别日志(默认级别)
            test1.info("信息级别日志输出");
            //config,配置级别日志
            test1.config("配置级别日志输出");
            //fine(没有问题的日志)
            test1.fine("没啥问题");
            //finer(比没有问题更没问题)
            test1.finer("更加没啥问题了!");
            //finest(最低级别)
            test1.finest("超级ok");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    解释

    从quickstart中我们发现所有打印的级别中除了前三项以外都没有打印,也就是说从config开始的级别日志对于重要程度上比较低,所以不需要进行打印,这也是默认的配置,我们可用对默认配置进行修改
    在jdk9及以上日志输出级别的配置文件在conf/logging.properties
    在这里插入图片描述

    修改打印日志的级别

    简单设置日志级别

    调用setLevel方法

    final Logger test1 = Logger.getLogger("test1");
    test1.setLevel(Level.FINEST);
    
    • 1
    • 2

    这里我们将级别设置为FINEST但是实际上也只会打印到CONFIG为止,后续从FINE开始的都不会被打印
    在这里插入图片描述

    使用Level.ALL打印所有日志级别

    即使我们使用.setLevel(Level.ALL)也仅能打印到CONFIG级别为止,和上面的结果是一样的!
    那如何才能真正打印所有的日志级别呢(请看下面)

    打印所有级别(解决)

    核心

    我们不仅要设置原始的Logger对象的日志级别,我们还要让Logger添加处理器(Handler)以及日志格式化器(Formatter)

            //获取logger对象
            final Logger test1 = Logger.getLogger("test1");
            //添加handler
            final ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setFormatter(new SimpleFormatter());
            test1.addHandler(consoleHandler);
            consoleHandler.setLevel(Level.ALL);
            //修改日志级别
            test1.setLevel(Level.ALL);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里我们添加了SimpleFormatter作为我们的Formatter,添加了ConsoleHandler作为Handler,这也是JUL的默认设置的,经过我们查询就能发现
    在这里插入图片描述
    再设置完Handler之后我们还需要设置Handler的输出级别,因为真实处理时Handler进行处理的所以我们必须这样才能保证设置起效

    完整代码
    @Test
        void contextLoads() {
            //获取logger对象
            final Logger test1 = Logger.getLogger("test1");
            //添加handler
            final ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setFormatter(new SimpleFormatter());
            test1.addHandler(consoleHandler);
            consoleHandler.setLevel(Level.ALL);
            //修改日志级别
            test1.setLevel(Level.ALL);
            //严重级别日志(最高级别)
            test1.severe("最高严重级别的日志输出");
            //警告级别日志
            test1.warning("警告类型的日志输出");
            //信息级别日志(默认级别)
            test1.info("信息级别日志输出");
            //config,配置级别日志
            test1.config("配置级别日志输出");
            //fine(没有问题的日志)
            test1.fine("没啥问题");
            //finer(比没有问题更没问题)
            test1.finer("更加没啥问题了!");
            //finest(最低级别)
            test1.finest("超级ok");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    了解JUL的Handler

    通过查询我们得到
    在这里插入图片描述

    1. ConsoleHandler:控制台输出处理器
    2. FileHandler:文件处理器
    3. MemoryHandler:内存处理器
    4. SocketHandler:网络socket处理器
    5. SLF4JBridgeHandler:slf4j的桥梁转化处理器

    ConsoleHandler

    此Handler将日志记录发布到System.err 。默认情况下, SimpleFormatter用于生成简短摘要

    FileHandler

    简单的文件记录Handler程序。
    FileHandler可以写入指定的文件,也可以写入旋转的文件集

    MemoryHandler

    在内存中的循环缓冲区中缓冲请求的Handler程序。
    通常,此Handler只是将传入的LogRecords存储到其内存缓冲区中并丢弃早期的记录。这种缓冲非常方便并且避免了格式化成本。在某些触发条件下, MemoryHandler会将其当前缓冲区内容推送到目标Handler ,该目标通常会将它们发布到外部

    SocketHandler

    简单的网络日志Handler 。
    LogRecords发布到网络流连接。默认情况下, XMLFormatter类用于格式化。

    SLF4JBridgeHandler

    将所有 JUL 日志记录桥接/路由到 SLF4J API。
    本质上,这个想法是在根记录器上安装一个SLF4JBridgeHandler的实例作为系统中唯一的 JUL 处理程序。随后,SLF4JBridgeHandler 实例会将所有 JUL 日志记录重定向到基于以下级别映射的 SLF4J API:

    • FINEST -> TRACE
    • FINER -> DEBUG
    • FINE -> DEBUG
    • INFO -> INFO
    • WARNING -> WARN
    • SEVERE -> ERROR

    向文件中输出日志

    初始化一个FileHandler以写入给定的文件名,并带有可选的附加。
    FileHandler是基于LogManager属性(或其默认值)配置的,可以写入的数据量没有限制,因此请谨慎使用。(写入过多日志会导致文件过大)

        @Test
        void test() throws IOException {
            final Logger test2 = Logger.getLogger("test2");
            /**
             * 参数
             * pattern - 输出文件的名称
             * append – 指定附加模式
             */
            final FileHandler fileHandler = new FileHandler("D:\\log_test.log", true);
            fileHandler.setFormatter(new SimpleFormatter());
            fileHandler.setLevel(Level.ALL);
            test2.setLevel(Level.ALL);
            test2.addHandler(fileHandler);
            test2.severe("严重错误");
            test2.info("信息级别日志输出");
            test2.config("配置级别日志输出");
            test2.fine("没啥问题");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    Logger之间的父子关系

    JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。并父子关系通过名称来关联。默认子Logger会继承父Logger的属性。

    自定义继承关系

    自定义的继承关系取决于你的名称的层级关系

        @Test
        void test2(){
            final Logger blog = Logger.getLogger("a.b");
            final Logger clog = Logger.getLogger("a.b.c");
            final Logger parent = clog.getParent();
            System.out.println(parent.getName());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    去除继承关系

    我们使用.setUseParentHandlers(false);进行设置即可去除继承关系进行自定义设置

    JUL日志格式化

    我们可用通过实现Formatter自定义日志格式化的方式
    做法很简单,实现Formatter接口,然后模仿SimpleFormatter写就可以了,当然我们也可以使用内部类的方式来进行实现

    方法

    1. record.getLevel():返回日志级别
    2. record.getInstant() :返回时间戳(日期)
    3. record.getLoggerName() :返回日志名称
    4. record.getMessage() :返回日志信息
    5. record.getMillis() :返回时间戳(毫秒)
    6. record.getParameters() :返回日志传参
    7. record.getSourceClassName() :返回日志所在类
    8. record.getSourceMethodName():返回日志所在方法

    实例

        @Test
        void test2() {
            Logger clog = Logger.getLogger("a.b.c");
            ConsoleHandler consoleHandler = new ConsoleHandler();
            consoleHandler.setFormatter(new DefineFormatter());
            consoleHandler.setLevel(Level.ALL);
            clog.setLevel(Level.ALL);
            clog.setUseParentHandlers(false);
            clog.addHandler(consoleHandler);
            clog.severe("严重的!!");
            clog.info("信息。。。。");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    自定义配置文件

    如何读取配置文件

    1.获取logManager对象
    final LogManager logManager = LogManager.getLogManager();
    
    • 1
    2.读取配置
    logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("文件名"))
    
    • 1

    QuickStart

    1.在resource下放置配置文件

    在这里插入图片描述

    handlers= java.util.logging.ConsoleHandler
    
    .level= FINE
    
    java.util.logging.FileHandler.pattern = %h/java%u.log
    java.util.logging.FileHandler.limit = 50000
    java.util.logging.FileHandler.count = 1
    
    java.util.logging.FileHandler.maxLocks = 100
    java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
    
    
    java.util.logging.ConsoleHandler.level = FINE
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
    com.xyz.foo.level = SEVERE
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    2.设置读取配置文件
    final LogManager logManager = LogManager.getLogManager();
    //读取配置文件
    logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties"));
    
    • 1
    • 2
    • 3
    3.输入日志
        @Test
        void test3() throws IOException {
            final LogManager logManager = LogManager.getLogManager();
            //读取配置文件
            logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties"));
            final Logger test4 = Logger.getLogger("test4");
            test4.fine("fine .....");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    关于配置文件

    1. handlers:用于指定rootLogger处理器
    2. java.util.logging.FileHandler.pattern:指定文件处理器的位置
    3. java.util.logging.FileHandler.limit :单文件大小限制
    4. java.util.logging.FileHandler.count :指定能够报错多少个日志文件
    5. java.util.logging.FileHandler.maxLocks :指定锁的数量
    6. java.util.logging.FileHandler.formatter :制定格式化器
    7. java.util.logging.ConsoleHandler.level :指定输出级别
    8. java.util.logging.FileHandler.append:指定是否追加写(true表示追加写)
    指定设置单独的handler

    例如我们要指定a.b.c的handler为ConsoleHandler

    a.b.c.handler:java.util.logging.ConsoleHandler
    
    • 1

    当然其他单独的配置也是这样!

    Filter过滤器

    设置过滤器使用setFilter方法进行设置,我们需要自己进行定义实现

    .setFilter(record -> {});
    
    • 1

    我们通过对LogRecord 对象用于在日志框架和各个日志处理程序之间传递日志请求。 进行自定义实现,通常来说我们会使用Filter设置个性化的实现例如设置在哪段时间内输出日志,而其他时间不输出

    打印异常堆栈

    对于报错信息我们应该将其异常打印出来
    需要使用throwing方法进行捕获抛出
    需要注意的是我们需要将级别设置为Finer!

    logger.throwing("logger名称","方法名",error);
    
    • 1
  • 相关阅读:
    docker部署Jenkins(Jenkins+Gitlab+Maven实现CI/CD)
    解决:chrome无痕模式下找不到插件问题
    快速排序知识总结
    NFA转换成DFA的方法——子集法
    【艾特淘】淘宝女装玩法内容化,“搭配购”实操技巧分享
    金典成为饿了么小蓝盒首个低碳“盒”伙人:战略合作迎绿色亚运
    河道水文标尺识别系统
    领域驱动设计——领域的整体设计
    Variable (mathematics)
    Matlab:在不同区域设置之间共享代码和数据
  • 原文地址:https://blog.csdn.net/qq_51553982/article/details/126616020