日志框架
- JUL日志框架
 - 入门程序
 - Logger直接的父子继承关系
 - Formatters
 - 独立的配置文件进行编写日志
 - 使用日志打印异常
 
学习日志无非就是为了四大目的:
- 问题诊断,调试程序;
 - 错误定位,定位数据问题、网络问题、内存问题等错误位置;
 - 数据分析,点击行为、兴趣爱好等构成日志,方便下次基于推荐。
 
主流日志框架:
- 日志实现(具体干活的):JUL(Java Util Logging)、Logback、log4j、log4j2.
 - 日志门面(指定规则的):JCL(Jakarta Commons Logging)、slf4j(Simple Logging Facade For Java).
 
JUL日志框架
JUL 全称 Java Util Logging 是Java原生的日志框架,使用时不需要另外引入第三方类库,相对于其他日志框架来说使用方便,学习简单,能够在小型应用中灵活使用。

JUL 的实现架构
在 JUL 中有以下关键组件:
Loggers(日志):被称为记录器,应用程序通过获取 Logger 对象,调用其API来发布日志信息,用于记录系统或应用程序组件的消息;

Appenders(输出源):也被称为 Handlers(处理器),每个Logger都会关联一组Handlers,Logger会将日志交给关联的 Haderler 处理,由 Handler 负责将日志做记录,Handlers 在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。

Layouts(布局):也被称为 Formatters,它负责对日志事件中的数据进行转换和格式化。Layout 决定了数据在一条日志记录中的最终形式。

Level(级别):每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,可以将 Level 和 Loggers 联系,Appenders 做关联以便我们过滤消息。

Filters(过滤器):根据需要定制哪些信息会被记录,哪些信息会被放过。

入门程序
有关日志对应的配置信息在所使用的 jdk 的 conf 目录下的 logging.properties 文件中。

    // 获取一个 logger 对象
    private static final Logger LOGGER = Logger.getLogger("mylog");
    @Test
    public void testJUL() throws IOException {
        // 去掉默认配置
        LOGGER.setUseParentHandlers(false);
		//创建一个控制台处理器,并设置输出形式和设置日志级别
        Handler handler = new ConsoleHandler();
        handler.setFormatter(new SimpleFormatter());
        handler.setLevel(Level.ALL);
		
		// 创建一个文件处理器,并设置输出形式和设置日志级别
        Handler fileHandler = new FileHandler("D://logs/jul-001.log",true);
        fileHandler.setLevel(Level.WARNING);
        fileHandler.setFormatter(new SimpleFormatter());
		// 添加日志处理器,可以添加多个,多个都会执行。
        LOGGER.addHandler(fileHandler);
        LOGGER.addHandler(handler);
        LOGGER.setLevel(Level.ALL);
        LOGGER.severe("This is a severe message");
        LOGGER.warning("This is a warning message");
        LOGGER.info("This is a info message");
        LOGGER.fine("This is a fine message");
        LOGGER.finer("This is a finer message");
    }
 
注意:任何一个 logger 都是单例的,前提是名字相同。如下所测试:

Logger直接的父子继承关系
Logger 之间存在父子继承关系,由上面可以知道,Logger 对象是根据传进去的名字而创建的,是单例的。

 默认条件下,子Logger 会继承父 Logger 中的属性(配置)。
 使用下面语句可以不继承父类的配置:
logger.setUseParentHandlers(false);
 
下面代码举例看看如何继承的父类配置:
    @Test
    public void testPrarent(){
        Logger logger = Logger.getLogger(TestJUL.class.getName());// com.ncpowernode.test.TestJUL
        Logger ncLogger = Logger.getLogger("com.ncpowernode.test");
        // 不使用 parent 的配置
        ncLogger.setUseParentHandlers(false);
		
		// 创建一个新的控制台处理器,并配置输出格式和级别
        Handler handler = new ConsoleHandler();
        handler.setFormatter(new SimpleFormatter());
        handler.setLevel(Level.ALL);
		
		// 父类进行配置,子类没有配置
        ncLogger.addHandler(handler);
        ncLogger.setLevel(Level.FINE);
        ncLogger.info("info");
        ncLogger.fine("fine");
		
		// 子类没有配置,按理说输出不了这条信息
        logger.fine("This is a fine message of son");
    }
输出结果:
2月 21, 2023 7:38:58 下午 com.ncpowernode.test.TestJUL testPrarent
信息: info
2月 21, 2023 7:38:58 下午 com.ncpowernode.test.TestJUL testPrarent
详细: fine
2月 21, 2023 7:38:58 下午 com.ncpowernode.test.TestJUL testPrarent
详细: This is a fine message of son
 
Formatters
可以看 Formatter 的一个实现类 SimpleFormatter类中的 format 方法的返回,可推测其如何实现的格式转换。
return String.format(format,
                             zdt,
                             source,
                             record.getLoggerName(),
                             record.getLevel().getLocalizedLevelName(),
                             message,
                             throwable);
 
看其返回值可以知道,无非是通过掉用 String 类中的 format 静态方法进行字符串转换的。
日期处理格式,%tx,其中x代表着日期转换符,以下是一些日期转换符的一些标志:

测试:

独立的配置文件进行编写日志
库中自行的配置是在 jdk 的主目录下的 conf 目录下的 logging.properties 配置文件中进行配置的,那能否进行自行编写配置文件进行配置呢?
通过源码可以发现,配置文件的读取和解析是通过 LogManager 对象进行实现的,然后通过读取的配置文件对 Logger 进行基本的管理配置。
下面是源码中 LogManager 对象的创建方式
LogManager manager = LogManager.getLogManager();
 


了解了源码,可以自行写配置文件然后自己读取了。
首先咱自己建一个配置文件,下面是文件内容:
 
以下是测试代码:
    @Test
    public void testLogManager() throws IOException {
        LogManager logManager = LogManager.getLogManager();
        //logManager.readConfiguration(new BufferedInputStream(TestJUL.class.getClassLoader().getResourceAsStream("logstudy.properties")));
        logManager.readConfiguration(new BufferedInputStream(new FileInputStream("src/main/resources/logstudy.properties")));
        Logger logger = Logger.getLogger(TestJUL.class.getName());
        logger.fine("This is a fine message!!!");
    }
 
结果展示:

使用日志打印异常
通过 Logger 类中的 throwing 方法的源码可以知道,如果需要打印异常日志,级别必须是FINER,否则直接返回结束该方法。

所以我们自行配置文件:
# RootHandler
# 开始清楚一个Logger可以搭配多个处理器handler
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
.level= FINER
java.util.logging.FileHandler.pattern = D://logs/java%u.log
java.util.logging.FileHandler.append = true # 是追加还是覆盖,true则是追加,false则是覆盖
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 = FINER
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 灵活进行转换
java.util.logging.ConsoleHandler.formatter.format = %4$s: %5$s [%1$tc] %6$s%n
 
然后编写代码测试:
	@Test
    public void testLogManager() throws IOException {
        LogManager logManager = LogManager.getLogManager();
        //logManager.readConfiguration(new BufferedInputStream(TestJUL.class.getClassLoader().getResourceAsStream("logstudy.properties")));
        logManager.readConfiguration(new BufferedInputStream(new FileInputStream("src/main/resources/logstudy.properties")));
        Logger logger = Logger.getLogger(TestJUL.class.getName());
        logger.finer("This is a finer message!!!");
        try{
            int i = 1/0;
        }catch(ArithmeticException e){
            logger.throwing(TestJUL.class.getName(),"testLogManager",e);
        }
    }
 
结果:



















![[MySQL]基本数据类型及表的基本操作](https://img-blog.csdnimg.cn/img_convert/cb75587e0710f47888e68e907eb130f2.gif)
