@Slf4j日志

1.日志

Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。

Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联

Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。

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

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

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

用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。

在Handler在输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,Handler会将日志内容输出到指定位置(日志文件、控制台等)。Handler在输出日志时会使用Layout,将输出内容进行排版。

1.1 入门案例

package top.onefine;

import org.junit.Test;

import java.util.logging.Level;
import java.util.logging.Logger;

public class JULTest {
    // Java获取当前类名的两种方法
    //  - 适用于非静态方法:this.getClass().getName()
    //  - 适用于静态方法:Thread.currentThread().getStackTrace()[1].getClassName()

    @Test
    public void testQuick() {
        // 1. 获取日志记录器对象,参数需要一个唯一标识
        Logger logger = Logger.getLogger(this.getClass().getName());
        // 2. 日志记录输出
        logger.info("hello jul");  // 日志级别:info

        // 使用通用方法进行日志记录
        logger.log(Level.INFO, "info msg");

        // 通过占位符的方式输出变量的值
        String name = "one fine";
        int age = 18;
        logger.log(Level.INFO, "用户信息:{0}, {1}", new Object[] {name, age});
    }
}

1.2 日志的级别

jul中定义的日志级别

* java.util.logging.Level中定义了日志的级别:
	SEVERE(最高值)			# 严重
	WARNING					# 警告
	INFO (默认级别)			# 信息
	
	CONFIG					# 配置
	FINE					# 详细
	FINER					# 较详细
	FINEST(最低值)			# 非常详细

* 还有两个特殊的级别:
	OFF,可用来关闭日志记录。
	ALL,启用所有消息的日志记录。

虽然我们测试了7个日志级别但是默认只实现info以上的级别

package top.onefine;

import org.junit.Test;

import java.util.logging.Logger;

public class JULTest {

    @Test
    public void testLogLevel() {
        // 1. 获取日志记录器对象,参数需要一个唯一标识
        Logger logger = Logger.getLogger(this.getClass().getName());
        // 2. 日志记录输出
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        // 默认情况下后面将不会输出到控制台(被过滤)
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
}

自定义日志级别配置

package top.onefine;

import org.junit.Test;

import java.io.IOException;
import java.util.logging.*;

public class JULTest {

    // 自定义日志级别
    @Test
    public void testLogConfig() throws IOException {
        // 1. 获取日志记录器对象,参数需要一个唯一标识
        Logger logger = Logger.getLogger(this.getClass().getName());

        // 关闭系统默认配置
        logger.setUseParentHandlers(false);

        /* 自定义配置日志级别
         */
        // 创建ConsoleHandler  控制台输出
        ConsoleHandler consoleHandler = new ConsoleHandler();

        // 创建简单格式formatter转换对象
        SimpleFormatter simpleFormatter = new SimpleFormatter();

        // 进行关联
        consoleHandler.setFormatter(simpleFormatter);
        logger.addHandler(consoleHandler);

        // 配置日志具体级别
        logger.setLevel(Level.ALL);
        consoleHandler.setLevel(Level.ALL);

        /* 输出日志到文件
         */
        // 创建FileHandler  文件输出
        FileHandler fileHandler = new FileHandler("d:/logs/jul.log");// 参数为日志文件放置位置,注意处理异常

        // 进行关联
        fileHandler.setFormatter(simpleFormatter);
        logger.addHandler(fileHandler);

        // 配置日志具体级别
        logger.setLevel(Level.ALL);
        consoleHandler.setLevel(Level.ALL);

        // 2. 日志记录输出
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
}

1.3 Logger之间的父子关系

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

package top.onefine;

import org.junit.Test;

import java.util.logging.*;

public class JULTest {

    // Logger对象父子关系
    @Test
    public void testLogParent() {
        Logger logger1 = Logger.getLogger("top.onefine");
        Logger logger2 = Logger.getLogger("top");
        System.out.println("logger2" + ((logger1.getParent() == logger2) ? "是" : "不是") + "logger1的父亲");  // true
        System.out.println("logger2 parent: " + logger2.getParent() + ", " +
                "name: " + logger2.getParent().getName());

        /* 输出:
            logger2是logger1的父亲
            logger2 parent: java.util.logging.LogManager$RootLogger@79bc6541, name:
         */
        // LogManager$RootLogger是所有日志记录器的顶级父元素,name为空串""


        /* 配置logger2的日志级别
         */

        // 关闭logger2默认配置
        logger2.setUseParentHandlers(false);
        // 创建ConsoleHandler  控制台输出
        ConsoleHandler consoleHandler = new ConsoleHandler();
        // 创建简单格式formatter转换对象
        SimpleFormatter simpleFormatter = new SimpleFormatter();
        // 进行关联
        consoleHandler.setFormatter(simpleFormatter);
        logger2.addHandler(consoleHandler);
        // 配置日志具体级别
        logger2.setLevel(Level.ALL);
        consoleHandler.setLevel(Level.ALL);

        logger1.severe("severe");
        logger1.warning("warning");
        logger1.info("info");
        logger1.config("config");
        logger1.fine("fine");
        logger1.finer("finer");
        logger1.finest("finest");
    }
}

结果:

logger2是logger1的父亲
logger2 parent: java.util.logging.LogManager$RootLogger@79bc6541, name: 
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
严重: severe
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
严重: severe
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
警告: warning
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
警告: warning
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
信息: info
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
信息: info
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
配置: config
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
详细: fine
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
较详细: finer
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
非常详细: finest

1.4 日志的配置文件

默认配置文件路径$JAVAHOME\jre\lib\logging.properties:

// C:\Program Files\Java\jre1.8.0_191\lib\logging.properties


############################################################
#  	Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.  
# For example java -Djava.util.logging.config.file=myfile
############################################################

############################################################
#  	Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler 
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
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.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format 
# to print one-line log message like this:
#     <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

配置文件resources/logging.properties:

# 指定RootLogger(顶级父类元素)的处理器(获取时设置)为:ConsoleHandlerFileHandler  即同时指定
#handlers= java.util.logging.ConsoleHandler, java.util.logging.FileHandler

# RootLogger顶级父类元素的日志等级:ALL
.level= ALL

## 文件处理器handle对象
# 输出日志级别
java.util.logging.FileHandler.pattern = d:/logs/java%u.log
# 输出日志文件限制大小(50000字节)
java.util.logging.FileHandler.limit = 50000
# 输出日志文件限制个数,5表示:0 - 4
java.util.logging.FileHandler.count = 1
# 以追加的方式添加日志内容,默认false
java.util.logging.FileHandler.append = true
# 输出日志格式,这里是XML
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
#java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

## 控制台处理器handle对象
# 输出日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定输出日志消息格式对象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定此控制台处理器handler对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 指定日志消息格式:五月 17, 2020 8:16:07 上午 top.onefine.JULTest testLogProperties 默认
#java.util.logging.ConsoleHandler.format = %4$s: %5$s [%1$tc]%n


## 自定义Logger, name = top.onefine
# 指定处理器
top.onefine.handlers = java.util.logging.ConsoleHandler
# 设置日志级别
top.onefine.level = CONFIG
# 关闭默认配置(忽略父日志设置)
top.onefine.useParentHanlders = false

java/top/onefine/JULTest.java:

package top.onefine;

import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.*;

public class JULTest {

    // 加载自定义配置文件
    @Test
    public void testLogProperties() throws IOException {
        // 读取配置文件,通过类加载器
        InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
        // 创建LogManager
        LogManager logManager = LogManager.getLogManager();
        // 通过LogManager加载配置文件
        logManager.readConfiguration(ins);  // 注意处理异常

        // 创建日志记录器
        Logger logger = Logger.getLogger(this.getClass().getName());

        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
}

1.5 日志原理解析

初始化LogManager
-LogManager加载logging.properties配置
-添加Logger到LogManager
从单例LogManager获取Logger
设置级别Level,并指定日志记录LogRecord
Filter提供了日志级别之外更细粒度的控制
Handler是用来处理日志输出位置
Formatter是用来格式化LogRecord的

JCL 学习【了解】

JCL全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。

它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog),所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk自带的日志(JUL)

JCL 有两个基本的抽象类:

Log:基本记录器
LogFactory:负责创建Log实例

Hello World

延续编程领域的传统,这里用一个例子说明使用SLF4J最简单的方式输出"Hello world"。首先,获取一个HelloWorld类的logger日志对象。这个logger对象反过来用于记录"Hello World"消息。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

典型的使用模式

下面的示例代码说明了SLF4J一个典型的使用模式。注意第15行的占位符{}的使用。参考FAQ的问题"What is the fastest way of logging?"了解更多详细信息。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
  
  public class Wombat {
   
    final Logger logger = LoggerFactory.getLogger(Wombat.class);
    Integer t;
    Integer oldT;
 
   public void setTemperature(Integer temperature) {
    
     oldT = t;        
     t = temperature;

     logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

     if(temperature.intValue() > 50) {
       logger.info("Temperature has risen above 50 degrees.");
     }
   }
 } 

在部署时绑定日志框架

正如之前提到的,SLF4J支持不同的日志框架。SLF4J发布包附带的几个jar包被称为“SLF4J绑定”,每一个绑定对应一种支持的框架。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值