Java日志技术
1.日志的概念
1.1 日志文件种类
- 调试日志
- 系统日志
2.Java日志框架
2.1 为什么要使用日志框架
- 软件复杂,设计的知识、内容、为题太多。
- 小系统不需要使用日志框架, 使用别人的框架你就可以集中精力完成系统的业务逻辑。
2.2 流行的日志框架[日志门面技术]
日志门面技术,就是面向接口编程。多种日志框架无缝切换。[类似JDBC]
冷门:JCL是门面技术但也提供了实现,slf4j也提供了实现但是也不用。log4j是实现技术但也提供了门面API但是一般也不用 。
2.2.1 日志门面
- JCL、slf4j
日志门面的优点
- 1.面向接口编程,减少耦合 [通过导入不同的日志实现类,灵活切换框架,统一配置便于项目日志管理]
- 2.统一的API,并隐藏了不同框架的底部的细节,隐藏了日志实现框架 的API。方便学习及使用。
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
3). Layouts
2. 源码分析
- org.apache.log4j.logger
- org.apache.log4j.logManager [读取配置信息]
- 在Loader.getResource();读取配置文件
- OptionConverter.selectAndConfigure();加载配置文件内部信息
- 然后把配置信息都加到rootLogger上
- configureRootCategory()
- parseCategory()
- parseCategory()解析信息 [StringTokenizer st = new StringTokenizer(value, ",");]
==不知道配置文件写什么就在这查==。<<学习框架方法论>>
除了这里还有就是ognl表达式 set方法
3.其他
3.1 Loglog: Log4j内置的日志的开关
3.2 Layout的使用
通过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
需要指定一个输出文件
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的
- 实验前
没有打印123456
- 实验后
打印123456
可见log4j是==通过包名来判断如何使用自定义的日志记录器==
- 原理
日志打印logger对象通过包名与配置文件的多个自定义的logger对比,如果没有找到就用默认的rootlogger。
3.3 JCL[门面框架] [只支持这几种所以淘汰了]
门面框架出现背景: 初期小项目用jul-> 后期用log4j 需要切换框架,麻烦
@Test
public void testQuickJCL(){
Log log = LogFactory.getLog(JCLTest.class);
log.info(123456);
}
- 1.没有log4j依赖时,默认使用jul
- 有log4j依赖时,默认使用log4j
3.3.1 原理
// 查找的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 日志桥接技术
3.4.3 [桥接技术]死循环的原因 [桥接不能和适配器同时出现(jar包)]
3.5 logback[实现框架]
https://www.cnblogs.com/gavincoder/p/10091757.html
3.5.0 基础代码
依赖关系
<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异步日志原理
两种方式
- AsyncAppender
- AsyncLogger
<!-- log4j2异步日志需要提供的依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
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来进行日志的编程:
- commons-logging
- jdk-loging
现在想统一将日志的输出交给log4j1。
解决方法:[两种]
- 将上面的日志系统全部转换到slf4j [ 桥接技术 ]
- 移除commons-logging(可以不移除),使用jcl-over-slf4j(桥接)将commons-logging的底层日志输出切换到sl4j.
- 使用jul-to-slf4j(桥接),将jul的日志输出切换到slf4j.
- 让slf4j选择log4j1作为底层日志输出
- 加入slf4j-api.jar
- 加入slf4j-log4j12.jar
- 加入log4j.jar