Java日志框架 总结 slf4j、log4j、logback

目录

Java日志技术

1.日志的概念

1.1 日志文件种类

  • 调试日志
  • 系统日志

2.Java日志框架

2.1 为什么要使用日志框架

  1. 软件复杂,设计的知识、内容、为题太多。
  2. 小系统不需要使用日志框架, 使用别人的框架你就可以集中精力完成系统的业务逻辑。

2.2 流行的日志框架[日志门面技术]

日志门面技术,就是面向接口编程。多种日志框架无缝切换。[类似JDBC]

冷门:JCL是门面技术但也提供了实现,slf4j也提供了实现但是也不用。log4j是实现技术但也提供了门面API但是一般也不用 。

2.2.1 日志门面
  • JCL、slf4j
日志门面的优点
  • 1.面向接口编程,减少耦合 [通过导入不同的日志实现类,灵活切换框架,统一配置便于项目日志管理]
  • 2.统一的API,并隐藏了不同框架的底部的细节,隐藏了日志实现框架 的API。方便学习及使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxkplitf-1630495502298)(/Users/admin/Documents/my-note/image-old/image-20200405110730005.png)]

2.2.2 日志实现
  • JUL[jdk自带]、logback[spring默认]、log4j、log4j2[性能最好]

3. 日志框架从入门到不想入门

3.0 所有的配置文件

3.0.1 JUL [核心是logManager以及Logger对象代码]
## RootLogger处理器(获取时设置)
handlers= java.util.logging.ConsoleHandler
## RootLogger处理器(打印日志时设置)
.handlers= RootLogger.java.util.logging.FileHandler
# RootLogger日志等级
.level= INFO

## TestLog日志处理器
TestLog.handlers= java.util.logging.FileHandler
# TestLog日志等级
TestLog.level= INFO
# 忽略父日志处理
TestLog.useParentHandlers=false

## 控制台处理器
# 输出日志级别
java.util.logging.ConsoleHandler.level = INFO
# 输出日志格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter


## 文件处理器
# 输出日志级别
java.util.logging.FileHandler.level=INFO
# 输出日志格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 输出日志文件路径
java.util.logging.FileHandler.pattern = D:\\TestLog.log
# 输出日志文件限制大小(50000字节)
java.util.logging.FileHandler.limit = 50000
# 输出日志文件限制个数
java.util.logging.FileHandler.count = 10
# 输出日志文件 是否是追加
java.util.logging.FileHandler.append=true
3.0.2 Log4j[核心是配置文件以及Appender]
log4j.rootLogger=trace,console

# 自定义logger
log4j.logger.com.biturd = info, file
# 命名规范一般是包名,这个是自定义的
# 这个是继承rootLogger的 级别会覆盖,appender追加
log4j.logger.org.apache = error
# 日志灵活输出

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m %n


log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n
#指定日志输出的位置[ognl表达式]
log4j.appender.file.file = /logs/log4j.log
log4j.appender.file.encoding = UTF-8


log4j.appender.rollingFileAppender = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFileAppender.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n
#指定日志输出的位置[ognl表达式]
log4j.appender.rollingFileAppender.file = /logs/log4j.log
log4j.appender.rollingFileAppender.encoding = UTF-8
log4j.appender.rollingFileAppender.maxFileSize = 1MB
log4j.appender.rollingFileAppender.maxBackupIndex = 10

log4j.appender.dailyRollingFileAppender = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFileAppender.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n
#指定日志输出的位置[ognl表达式]
log4j.appender.dailyRollingFileAppender.file = /logs/log4j.log
log4j.appender.dailyRollingFileAppender.encoding = UTF-8
log4j.appender.dailyRollingFileAppender.datePattern = '.'yyyy-MM-dd-HH-mm-ss

log4j.appender.dbAppender = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.dbAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.dbAppender.driver = com.mysql.jdbc.Driver 
log4j.appender.dbAppender.url = jdbc:mysql:///test
# jdbc:mysql://localhost:3306/test
log4j.appender.dbAppender.user = root
log4j.appender.dbAppender.password = root
log4j.appender.dbAppender.sql = INSERT INTO LOGGING (log_date, log_level, location, message) VALUES ('%d{ISO8601}', '%-5p', '%C,%L', '%m')

3.1 JUL[实现框架]

两种方法输出日志
    logger.log(Level.INFO, "msg");  // 通用方法
    logger.info("123456"); // 专用方法  logger.[级别]("[信息]");
组件[非具体类]调用过程
- Application
	- Logger
		设置两个内容
		1. Level
		2. 日志消息内容
		- Handler
			指定输出路径[控制台、文件、网络]
			- Layouts[Formatters] (输出前进行格式化)
				- Filter[细粒度的日志拦截]
日志级别控制

一共七个日志级别,通过值来控制,默认只输出默认取值比Logger.INFO高的。 如果手动设置Logger的级别那么就输出比手动设置的高的级别。

Maven中的dependency如果是是test写在 main目录下面就会报错[导包找不到]

    @Test
    public void testQuick() throws Exception{
        Logger logger = Logger.getLogger("com.biturd.JULTest");  // 每一个日志记录器都需要有一个唯一标识
        logger.info("hello jul");
        logger.log(Level.INFO, "msg");

        String name = "123456";
        int a = 123456;
        logger.log(Level.INFO, "用户信息: 字符串{0},数字{1}",new Object[]{name,a});
    }
    public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
    public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
    public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
    public static final Level INFO = new Level("INFO", 800, defaultBundle);
    public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
    public static final Level FINE = new Level("FINE", 500, defaultBundle);
    public static final Level FINER = new Level("FINER", 400, defaultBundle);
    public static final Level FINEST = new Level("FINEST", 300, defaultBundle);	
	public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
    @Test   
    public void testLogLevel() throws Exception{
        Logger logger = Logger.getLogger("com.biturd.JULTest"); // 每一个日志记录器都需要有一个唯一标识 通常是全限定类名
        logger.severe("server");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }

在这里插入图片描述

底层的原理及自定义配置文件
- logger
	- demandLogger
		- LogManager.getLogManager()
			-ensureLogManagerInitialized() [加载配置文件]            
            	- readConfiguration
  
    public void readConfiguration() throws IOException, SecurityException {
        checkPermission();

        // if a configuration class is specified, load it and use it.
        String cname = System.getProperty("java.util.logging.config.class");
        if (cname != null) {
            try {
                // Instantiate the named class.  It is its constructor's
                // responsibility to initialize the logging configuration, by
                // calling readConfiguration(InputStream) with a suitable stream.
                try {
                    Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
                    clz.newInstance();
                    return;
                } catch (ClassNotFoundException ex) {
                    Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
                    clz.newInstance();
                    return;
                }
            } catch (Exception ex) {
                System.err.println("Logging configuration class \"" + cname + "\" failed");
                System.err.println("" + ex);
                // keep going and useful config file.
            }
        }

        String fname = System.getProperty("java.util.logging.config.file");
        if (fname == null) {
            fname = System.getProperty("java.home");
            if (fname == null) {
                throw new Error("Can't find java.home ??");
            }
            File f = new File(fname, "lib");
            f = new File(f, "logging.properties");
            fname = f.getCanonicalPath();
        }
        try (final InputStream in = new FileInputStream(fname)) {
            final BufferedInputStream bin = new BufferedInputStream(in);
            readConfiguration(bin);
        }
    }
    
 E:\Java\jre\lib\logging.properties   
 
 logManager是单例对象,所以前面后面所有的都是一个对象
 加载流肯定应用了。    
    @Test
    public void testLogProperties() throws Exception{
        // 加载配置文件
        InputStream ins =  JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
        // 创建logManager
        LogManager logManager = LogManager.getLogManager();
        // 通过logManager加载配置文件

		logManager.readConfiguration(ins);

        Logger logger = Logger.getLogger("com.biturd");
        
        logger.severe("server");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");

    }
配置文件总览
## RootLogger处理器(获取时设置)
handlers= java.util.logging.ConsoleHandler
## RootLogger处理器(打印日志时设置)
.handlers= RootLogger.java.util.logging.FileHandler
# RootLogger日志等级
.level= INFO

## TestLog日志处理器
TestLog.handlers= java.util.logging.FileHandler
# TestLog日志等级
TestLog.level= INFO
# 忽略父日志处理
TestLog.useParentHandlers=false

## 控制台处理器
# 输出日志级别
java.util.logging.ConsoleHandler.level = INFO
# 输出日志格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter


## 文件处理器
# 输出日志级别
java.util.logging.FileHandler.level=INFO
# 输出日志格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 输出日志文件路径
java.util.logging.FileHandler.pattern = D:\\TestLog.log
# 输出日志文件限制大小(50000字节)
java.util.logging.FileHandler.limit = 50000
# 输出日志文件限制个数
java.util.logging.FileHandler.count = 10
# 输出日志文件 是否是追加
java.util.logging.FileHandler.append=true

3.2 Log4j[实现框架]

0.代码示例
    @Test
    public void QuickStartLog4j(){
        // 不使用配置文件 用默认的方式
//        BasicConfigurator.configure();
        // 获取日志记录器对象
        Logger logger = Logger.getLogger(log4jTest.class);
        // 日志记录输出
        logger.info("hello log4j");

        logger.fatal("造成系统崩溃的严重错误");   
        // 常用的
        logger.error("不会让系统停止的错误信息");
        logger.warn("可能发生错误警告信息");
        logger.info("运行信息,数据连接、网络连接、IO、操作等");
        logger.debug("调试信息,一般在开发中使用,记录程序变量参数传递信息等");

        logger.trace("追踪信息,记录程序所有的流程信息");
    }
1.Log4j组件
  • Loggers [日志记录器] 控制日志的输出级别与日志是否输出
  • Appenders [输出端] 日志的输出方式
  • Layout [日志格式化] 控制日志的输出格式
1). Loggers日志记录器 [默认继承RootLogger]

早期叫Category,所以可以视为是Category类的别名

在这里插入图片描述

2). Appenders

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4FKTTiH-1630495502308)(/Users/admin/Documents/my-note/image-old/image-20200403092804928.png)]

3). Layouts

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NuWbtfpo-1630495502310)(/Users/admin/Documents/my-note/image-old/image-20200403093159621.png)]

2. 源码分析
- org.apache.log4j.logger
	- org.apache.log4j.logManager [读取配置信息]
	- 在Loader.getResource();读取配置文件
	- OptionConverter.selectAndConfigure();加载配置文件内部信息
		- 然后把配置信息都加到rootLogger上
		- configureRootCategory()
		- parseCategory()
			- parseCategory()解析信息 [StringTokenizer st = new StringTokenizer(value, ",");]
	

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UCENH4ER-1630495502311)(/Users/admin/Documents/my-note/image-old/image-20200403115422584.png)]

在这里插入图片描述

不知道配置文件写什么就在这查。<<学习框架方法论>>

除了这里还有就是ognl表达式 set方法

3.其他
3.1 Loglog: Log4j内置的日志的开关

在这里插入图片描述

3.2 Layout的使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uUPANV7w-1630495502319)(/Users/admin/Documents/my-note/image-old/image-20200404204902538.png)]

通过ognl表达式来指定。

log4j.rootLogger=trace,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout

log4j.appender.console.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n

# %l == %c+%t+%F+%L

在这里插入图片描述

3.3 Appender的使用

在这里插入图片描述

  • Console
  • File[两个子类,可以选择用父类或子类]
    • Rolling [按大小拆分]
    • DailyRolling [按时间拆分]
3.3.1 File

需要指定一个输出文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9LBENhsf-1630495502325)(/Users/admin/Documents/my-note/image-old/image-20200404210001421.png)]

log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n
#指定日志输出的位置[ognl表达式]
log4j.appender.file.file = /logs/log4j.log
log4j.appender.file.encoding = UTF-8
3.3.2 Rolling
log4j.appender.rollingFileAppender = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFileAppender.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n
#指定日志输出的位置[ognl表达式]
log4j.appender.rollingFileAppender.file = /logs/log4j.log
log4j.appender.rollingFileAppender.encoding = UTF-8
log4j.appender.rollingFileAppender.maxFileSize = 1MB
log4j.appender.rollingFileAppender.maxBackupIndex = 10  
# [超过十个会覆盖久远的日志]
3.3.3 DailyRolling
log4j.appender.dailyRollingFileAppender = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFileAppender.layout.conversionPattern = [%p]%r %l %d{yyyy-mm-dd HH:mm:ss.SSS} %m%n
#指定日志输出的位置[ognl表达式]
log4j.appender.dailyRollingFileAppender.file = /logs/log4j.log
log4j.appender.dailyRollingFileAppender.encoding = UTF-8
log4j.appender.dailyRollingFileAppender.datePattern = '.'yyyy-MM-dd-HH-mm-ss 
# [完全的,最合适的是按照天划分]
3.3.4 JDBCAppender
log4j.appender.dbAppender = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.dbAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.dbAppender.driver = com.mysql.jdbc.Driver 
log4j.appender.dbAppender.url = jdbc:mysql:///test
# jdbc:mysql://localhost:3306/test
log4j.appender.dbAppender.user = root
log4j.appender.dbAppender.password = root
log4j.appender.dbAppender.sql = INSERT INTO log(preject_name, create_date, log_level, category,file_name,thread_name,line,all_category, message) VALUES ('pro_name', '%d{yyyy-MM-dd HH:mm:ss}', '%p', '%c', '%F','%t', '%L' , '%l' ,'%m')
3.4 自定义logger
log4j.logger.com.biturd = info, file
# 命名规范一般是包名,这个是自定义的
# 这个是继承rootLogger的 级别会覆盖,appender追加
log4j.logger.org.apache = error
# 日志灵活输出

默认是继承rootlogger的

  • 实验前

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Skg5yDys-1630495502326)(/Users/admin/Documents/my-note/image-old/image-20200405082951614.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i55PYQ54-1630495502328)(/Users/admin/Documents/my-note/image-old/image-20200405083024130.png)]

没有打印123456

  • 实验后
    在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ECKDThc-1630495502333)(/Users/admin/Documents/my-note/image-old/image-20200405082824218.png)]

打印123456

可见log4j是通过包名来判断如何使用自定义的日志记录器

  • 原理

日志打印logger对象通过包名与配置文件的多个自定义的logger对比,如果没有找到就用默认的rootlogger。

3.3 JCL[门面框架] [只支持这几种所以淘汰了]

门面框架出现背景: 初期小项目用jul-> 后期用log4j 需要切换框架,麻烦

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wzt2g6Pq-1630495502378)(/Users/admin/Documents/my-note/image-old/image-20200405083911433.png)]

    @Test
    public void testQuickJCL(){
        Log log = LogFactory.getLog(JCLTest.class);
        log.info(123456);
    }
  • 1.没有log4j依赖时,默认使用jul

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G06jdQIr-1630495502382)(/Users/admin/Documents/my-note/image-old/image-20200405093515568.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-syR2SHug-1630495502385)(/Users/admin/Documents/my-note/image-old/image-20200405093429447.png)]

  • 有log4j依赖时,默认使用log4j

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UEM2lObh-1630495502387)(/Users/admin/Documents/my-note/image-old/image-20200405093613785.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UbPFndTo-1630495502388)(/Users/admin/Documents/my-note/image-old/image-20200405093843684.png)]

3.3.1 原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JEiU0OLG-1630495502390)(/Users/admin/Documents/my-note/image-old/image-20200405102418149.png)]

// 查找的log类的范围
private static final String[] classesToDiscover = {
            LOGGING_IMPL_LOG4J_LOGGER,
            "org.apache.commons.logging.impl.Jdk14Logger",
            "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
            "org.apache.commons.logging.impl.SimpleLog"
    };
// 查找log类的具体实现
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
            result = createLogFromClass(classesToDiscover[i], logCategory, true);
        }
// 利用反射技术根据名字字符串查找类
try {
                    c = Class.forName(logAdapterClassName, true, currentCL);
                } catch (ClassNotFoundException originalClassNotFoundException) {
    
// 如果找到了
                constructor = c.getConstructor(logConstructorSignature);
                Object o = constructor.newInstance(params);

                // Note that we do this test after trying to create an instance
                // [rather than testing Log.class.isAssignableFrom(c)] so that
                // we don't complain about Log hierarchy problems when the
                // adapter couldn't be instantiated anyway.
                if (o instanceof Log) {
                    logAdapterClass = c;
                    logAdapter = (Log) o;
                    break;
                }    
    
// 适配器将找到的log类的构造方法、以及log类的Name适配为实例属性。
        if (logAdapterClass != null && affectState) {
            // We've succeeded, so set instance fields
            this.logClassName   = logAdapterClassName;
            this.logConstructor = constructor;

            // Identify the <code>setLogFactory</code> method (if there is one)
            try {
                this.logMethod = logAdapterClass.getMethod("setLogFactory", logMethodSignature);
               .......
            }
	return logAdapter;

3.4 SLF4j[门面框架]

Note that SLF4J-enabling your library implies the addition of only a single mandatory dependency, namely slf4j-api.jar. If no binding is found on the class path, then SLF4J will default to a no-operation implementation.

slf4j意味着仅添加一个强制性依赖项,slf4j-api.jar。如果在类路径上没有找到绑定,他就默认无操作实现。[只是一套接口]
3.4.1 原理:

在这里插入图片描述

// 如果有多个实现就打印,并使用第一个实现框架

 if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
            Util.report("Class path contains multiple SLF4J bindings.");
            Iterator i$ = binderPathSet.iterator();

3.4.2 日志桥接技术

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UngUJ6DV-1630495502394)(/Users/admin/Documents/my-note/image-old/image-20200405232257770.png)]

3.4.3 [桥接技术]死循环的原因 [桥接不能和适配器同时出现(jar包)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ekVHT1OV-1630495502395)(/Users/admin/Documents/my-note/image-old/image-20200405231826182.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1W5SHKTY-1630495502398)(/Users/admin/Documents/my-note/image-old/image-20200405232029754.png)]

3.5 logback[实现框架]

https://www.cnblogs.com/gavincoder/p/10091757.html

3.5.0 基础代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8IOlcpJl-1630495502399)(/Users/admin/Documents/my-note/image-old/image-20200406071803831.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjObYrM4-1630495502401)(/Users/admin/Documents/my-note/image-old/image-20200406071937720.png)]

依赖关系

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>
    
    <!-- 只需要导入logback-classic就可以了,被依赖的会自动导入 -->
<?xml version="1.0" encoding="utf-8" ?>
<configuration debug="true">
    <property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] [%-5level] %logger{36} - %msg%n"></property>
    <property name="log_back" value="/logs"></property>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <!-- 日志保存的位置 -->
        <file>${log_dir}/logback.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
        <file>${log_dir}/logback.html</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5level] %logger{36} - %msg%n"</pattern>
            </layout>
        </encoder>
    </appender>

    <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log_dir}/logback.html</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
        </rollingPolicy>
    </appender>

    <root level="ALL">
        <appender-ref ref="console"></appender-ref>
    </root>
</configuration>
3.5.1 三个模块
  • logback-core: 其他两个模块的基础
  • logback-classic: log4j的改良版本[ 完整实现了slf4j的api ]
  • logback-access: 访问模块和servlet容器集成提供通过Http访问日志的功能
3.5.2 高阶用法 [ 过滤器及异步日志、自定义logger ]
   <!-- 过滤器用法 -->
	<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log_dir}/logback.html</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxFileSize>1MB</maxFileSize>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMisMatch>DENY</onMisMatch>
        </filter>
    </appender>

    <!-- 异步日志 -->
    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 指定一个具体的Appender -->
        <appender-ref ref="rollFile"/>
    </appender>

    <root level="ALL">
        <appender-ref ref="console"></appender-ref>
        <appender-ref ref="async"></appender-ref>
    </root>

    <!-- 自定义logger对象 additivity决定是否集成rootLogger -->
    <logger name="com.biturd" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
3.5.3 log4j格式转logback [ pattern变了 ]

在这里插入图片描述

3.5.4 logback-access使用

使用这个来替换tomcat或jetty的日志
在这里插入图片描述

3.6 log4j2[实现框架]

虽然也是日志门面,但是习惯上还是用slf4j做日志门面

1.log4j2日志门面用法[了解]
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.11.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.11.2</version>
        </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">
    <!--先定义所有的appender-->
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
        </console>
        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
        <File name="log" fileName="log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>
        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
        <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
        <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>
</configuration>
2. log4j2异步日志原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iTd2Op1i-1630495502409)(/Users/admin/Documents/my-note/image-old/image-20200414122159052.png)]

两种方式

  • AsyncAppender
  • AsyncLogger
        <!-- log4j2异步日志需要提供的依赖 -->
		<dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.3.4</version>
        </dependency>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjEX5Jc3-1630495502415)(/Users/admin/Documents/my-note/image-old/image-20200414123002440.png)]

3.7 SpringBoot中的应用

虽然使用默认logback,可以不导入logback.xml文件,而是在application.xml里面也能设置日志框架的信息,但是一般比较粗略,所以还是需要特定的配置文件的

SpringBoot的Starters包含了一系列可以集成到应用里面的依赖包,你可以一站式集成Spring及其他技术,而不需要到处找示例代码和依赖包。如你想使用Spring JPA访问数据库,只要加入spring-boot-starter-data-jpa启动器依赖就能使用了。https://blog.csdn.net/qq_35974759/article/details/87168257

Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。

依赖关系: 指向谁就依赖谁

3.8 代码总结

JUL
        Logger logger = Logger.getLogger("com.biturd.JULTest");  // 每一个日志记录器都需要有一个唯一标识
        logger.info("hello jul");
        logger.log(Level.INFO, "msg");
log4j
        // 不使用配置文件 用默认的方式
//        BasicConfigurator.configure();
        LogLog.setInternalDebugging(true);
        // 获取日志记录器对象
        Logger logger = Logger.getLogger(log4jTest.class);
        // 日志记录输出
        logger.info("hello log4j");
        logger.fatal("造成系统崩溃的严重错误");
        // 常用的
        logger.error("不会让系统停止的错误信息");
        logger.warn("可能发生错误警告信息");
        logger.info("运行信息,数据连接、网络连接、IO、操作等");
        logger.debug("调试信息,一般在开发中使用,记录程序变量参数传递信息等");

        logger.trace("追踪信息,记录程序所有的流程信息");
JCL
        Log log = LogFactory.getLog(JCLTest.class);
        log.info(123456);
slf4j[重点]
public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
    @Test
    public void testSlf4j(){
        LOGGER.warn("123456");
        LOGGER.error("123456");
    }
log4j2[自己的门面]
    public static final Logger LOGGER = LogManager.getLogger(log4j2Test.class);

    @Test
    public void testQuick() throws Exception{
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
slf4j的各种依赖关系[ 比较重要就再说一遍 ]
1. slf4j-jdk

在这里插入图片描述

2. slf4j-log4j

在这里插入图片描述

3. slf4j-logback

在这里插入图片描述

4.slf4j-log4j2

在这里插入图片描述

其他的比如桥接技术[ 详细的: https://blog.csdn.net/jeikerxiao/article/details/62423749 ]

使用slf4j统一各种日志
当项目是使用多种日志API时,可以统一适配到slf4j,然后通过slf4j适配底层实现。

中间使用slf4j进行统一项目中使用的多种日志框架的API,然后转发到slf4j,slf4j再底层用开发者想用的一个日志框架来进行日志系统的实现。从而达到了多种日志的统一实现。

  • 旧日志API到slf4j的适配器

  • slf4j到新日志实现的适配器


问题:目前的应用程序(application)中已经使用了以下的几种API来进行日志的编程:

  1. commons-logging
  2. jdk-loging
    现在想统一将日志的输出交给log4j1。

解决方法:[两种]

  1. 将上面的日志系统全部转换到slf4j [ 桥接技术 ]
    1. 移除commons-logging(可以不移除),使用jcl-over-slf4j(桥接)将commons-logging的底层日志输出切换到sl4j.
    2. 使用jul-to-slf4j(桥接),将jul的日志输出切换到slf4j.
  2. 让slf4j选择log4j1作为底层日志输出
    1. 加入slf4j-api.jar
    2. 加入slf4j-log4j12.jar
    3. 加入log4j.jar
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SLF4Jlog4jlogbackJava的三个不同的日志框架SLF4JJava的一个日志门面,它提供了一些通用的API,可以与不同的具体日志框架集成使用。log4j是一个具体的日志框架,它提供了丰富的功能和配置选项。logback则是由log4j的作者设计完成的一个日志框架,它拥有更好的特性,并且是SLF4J的原生实现。 区别如下: 1. SLF4J是一个日志门面,它只提供了一些通用的API,而不是具体的实现。它的作用是为了让开发人员可以在不同的日志框架之间进行切换和集成,而不需要修改代码。 2. log4j是一个具体的日志框架,它提供了丰富的功能和配置选项。log4j可以与SLF4J结合使用,需要提供一些对应的jar包。 3. logback是由log4j的作者设计完成的日志框架,它是SLF4J的原生实现。logback拥有更好的特性,并且可以完整地实现SLF4J的API。logback包括了三个模块:logback-core、logback-classic和logback-access,分别用于提供基础功能、改良版本以及与Servlet容器集成。 因此,SLF4J提供了通用的日志接口,log4j是其一个具体的实现,而logback则是log4j的改良版本,同时也是SLF4J的原生实现。根据具体需求和偏好,开发人员可以选择使用其的任意一个日志框架。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java日志框架SLF4Jlog4j以及logback的联系和区别](https://blog.csdn.net/weixin_30241919/article/details/101487496)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值