1. JUL学习
JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框 架使用方便,学习简单,能够在小型应用中灵活使用。
1.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
,将输出内容进行排版。
@Test
public void test01(){
Logger logger = Logger.getLogger("cn.quguai.JULTest");
logger.info("Hello JUL");
logger.log(Level.INFO, "info msg");
logger.log(Level.INFO, "message:{0}:{1}", new Object[]{"quguai", 1});
}
1.2 日志级别
java.util.logging.Level中定义了日志的级别:
SEVERE
(最高值)WARNING
INFO
(默认级别)CONFIG
FINE
FINER
FINEST
(最低值)
还有两个特殊的级别:
OFF
,可用来关闭日志记录。ALL
,启用所有消息的日志记录。
@Test
public void testLevel(){
Logger logger = Logger.getLogger("cn.quguai.JULTest");
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
1.3 自定义日志级别配置
@Test
public void testSetLevel() throws IOException {
Logger logger = Logger.getLogger("cn.quguai.JULTest");
// 关闭原有处理器
logger.setUseParentHandlers(false);
// 创建ConsoleHandle 和 SimpleFormatter 格式化器
ConsoleHandler consoleHandler = new ConsoleHandler();
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
logger.addHandler(consoleHandler);
consoleHandler.setFormatter(simpleFormatter); // 默认就是SimpleFormatter
// 创建FileHandle
FileHandler fileHandler = new FileHandler("./jul.log");
// 进行关联
logger.addHandler(fileHandler);
fileHandler.setFormatter(simpleFormatter);
// 设置默认级别
consoleHandler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
// fileHandler.setLevel(Level.ALL); 默认沿用logger的配置
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
1.4 Logger 父子关系
@Test
public void testLogParent(){
Logger logger1 = Logger.getLogger("cn.quguai");
Logger logger2 = Logger.getLogger("cn");
// 默认顶级Logger对象是 java.util.logging.LogManager$RootLogger
System.out.println(logger2.getParent());
// 修改logger2的配置
logger2.setUseParentHandlers(false);
ConsoleHandler consoleHandler = new ConsoleHandler();
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");
}
1.5 Logger 默认配置文件
文件路径:D:\jdk-11.0.10\conf\logging.properties
java.util.logging.Logger#getLogger() ->
java.util.logging.Logger#demandLogger ->
java.util.logging.LogManager#getLogManager ->
java.util.logging.LogManager#ensureLogManagerInitialized->
java.util.logging.LogManager#readPrimordialConfiguration ->
java.util.logging.LogManager#readConfiguration() ->
java.util.logging.LogManager#getConfigurationFileName
############################################################
# 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 # 当前用户目录下 java01.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
# Default number of locks FileHandler can obtain synchronously.
# This specifies maximum number of attempts to obtain lock file by FileHandler
# implemented by incrementing the unique field %u as per FileHandler API documentation.
java.util.logging.FileHandler.maxLocks = 100
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
1.6 自定义配置文件
# 顶级父元素的默认的处理器
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler // 追加文件管理器
# 顶级父元素RootLogger的默认输出级别
.level= ALL // 修改默认级别
# 文件配置路径
java.util.logging.FileHandler.pattern = ./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 #XML格式化器
java.util.logging.FileHandler.append = true
java.util.logging.ConsoleHandler.level = ALL # 修改默认级别
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8
@Test
public void testProperties() throws IOException {
InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
// LogManager 是一个单例对象
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(ins);
Logger logger = Logger.getLogger("cn.quguai.JULTest");
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
ins.close();
}
1.7 JUL配置文件详情
1.7.1 ConsoleHandler配置形式
java.util.logging.ConsoleHandler.encoding = UTF-8
1.7.2 配置文件格式化方式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
SurrogateLogger.getSimpleFormat 获取配置文件中的key值对应的格式化方式
1.7.3 追加文件
java.util.logging.FileHandler.append = true
1.7.4 自定义Logger
# 自定义Logger
cn.quguai.handlers = java.util.logging.FileHandler
cn.quguai.level = CONFIG
cn.quguai.useParentHandlers=false
1.8 日志原理解析
- 初始化LogManager
- LogManager加载logging.properties配置
- 添加Logger到LogManager
- 从单例LogManager获取Logger
- 设置级别Level,并指定日志记录LogRecord
- Filter提供了日志级别之外更细粒度的控制
- Handler是用来处理日志输出位置
- Formatter是用来格式化LogRecord的
2. Log4j
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
2.1 日志级别
级别 | 含义 |
---|---|
fatal | 指出每个严重的错误事件将会导致应用程序的退出。 |
error | 虽然发生错误事件,但仍然不影响系统的继续运行。 |
warn | 表明会出现潜在的错误情形。 |
info | 一般和在粗粒度级别上,强调应用程序的运行全程。 |
debug | 一般用于细粒度级别上,对调试应用程序非常有帮助。默认级别 |
trace | 是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。 |
推荐:实际开发中只会使用到最中间的四个步骤。
@Test
public void testQuick(){
Logger logger = Logger.getLogger(Log4jTest.class);
BasicConfigurator.configure();
// logger.setLevel(Level.INFO);
logger.fatal("fatal");
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug"); // 默认级别
logger.trace("trace");
}
2.2 Log4j 组件
Log4J 主要由 Loggers
(日志记录器)、Appenders
(输出端)和 Layout
(日志格式化器)组成。
其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件 等);Layout 控制日志信息的输出格式。
2.2.1 Loggers
日志记录器,负责收集处理日志记录,实例的命名就是类“XX”的full quailied name(类的全限定名), Logger的名字大小写敏感,其命名有继承机制:例如:name为org.apache.commons的logger会继承 name为org.apache的logger。
Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接 或者间接地继承自root。root logger可以用Logger.getRootLogger()方法获取。
但是,自log4j 1.2版以来, Logger 类已经取代了 Category 类。对于熟悉早期版本的log4j的人来说, Logger 类可以被视为 Category 类的别名。
2.2.2 Appenders
Appender 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j 常用的输出目的地 有以下几种:
输出端类型 | 作用 |
---|---|
ConsoleAppender | 将日志输出到控制台 |
FileAppender | 将日志输出到文件中 |
DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 |
RollingFileAppender | 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大 小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件 |
JDBCAppender | 把日志信息保存到数据库中 |
2.2.3 Layouts
布局器 Layouts用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。Log4j常用 的Layouts:
格式化器类型 | 作用 |
---|---|
HTMLLayout | 格式化日志输出为HTML表格形式 |
SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info - message) |
PatternLayout | 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式, 就是用默认的转换格式 |
2.3 配置文件详情
配置文件的后缀名称全部来源于对应的set方法注入,例如conversionPattern 使用SetConversionPattern方法完成
log4j.rootLogger = trace,console
# 指定appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定格式化信息
log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
2.4 配置文件源码
2.4.1 支持的配置文件
public class LogManager {
/** @deprecated */
public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
/** @deprecated */
public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
/** @deprecated */
public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
/** @deprecated */
public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
}
org.apache.log4j.LogManager ->
org.apache.log4j.helpers.OptionConverter#selectAndConfigure ->
org.apache.log4j.PropertyConfigurator ->
public class PropertyConfigurator implements Configurator {
protected Hashtable registry = new Hashtable(11);
protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
static final String CATEGORY_PREFIX = "log4j.category.";
static final String LOGGER_PREFIX = "log4j.logger.";
static final String FACTORY_PREFIX = "log4j.factory";
static final String ADDITIVITY_PREFIX = "log4j.additivity.";
static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
static final String APPENDER_PREFIX = "log4j.appender.";
static final String RENDERER_PREFIX = "log4j.renderer.";
static final String THRESHOLD_PREFIX = "log4j.threshold";
public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
private static final String INTERNAL_ROOT_NAME = "root";
}
2.4.2 rootLogger 解析原理
rootLogger 的配置源码 org.apache.log4j.PropertyConfigurator#parseCategory
void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {
LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
// 按照逗号进行分割
StringTokenizer st = new StringTokenizer(value, ",");
if (!value.startsWith(",") && !value.equals("")) {
if (!st.hasMoreTokens()) {
return;
}
// 第一个参数作为 level
String levelStr = st.nextToken();
LogLog.debug("Level token is [" + levelStr + "].");
if (!"inherited".equalsIgnoreCase(levelStr) && !"null".equalsIgnoreCase(levelStr)) {
logger.setLevel(OptionConverter.toLevel(levelStr, Level.DEBUG));
} else if (loggerName.equals("root")) {
LogLog.warn("The root logger cannot be set to null.");
} else {
logger.setLevel((Level)null);
}
LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
}
logger.removeAllAppenders();
// 后续的参数作为 appender
while(st.hasMoreTokens()) {
String appenderName = st.nextToken().trim();
if (appenderName != null && !appenderName.equals(",")) {
LogLog.debug("Parsing appender named \"" + appenderName + "\".");
Appender appender = this.parseAppender(props, appenderName);
if (appender != null) {
logger.addAppender(appender);
}
}
}
}
2.5 内部日志记录
public class LogLog {
public static void debug(String msg) {
if (debugEnabled && !quietMode) {
System.out.println("log4j: " + msg);
}
}
}
可以使用
LogLog.setInternalDebugging(true)
打开开关,这是LogLog的内部静态方法。
2.6 Layouts 格式
在 log4j.properties 配置文件中,我们定义了日志输出级别与输出端,在输出端中分别配置日志的输出 格式。
log4j 采用类似 C 语言的 printf 函数的打印格式格式化日志信息,具体的占位符及其含义如下:
形式 | 含义 |
---|---|
%m | 输出代码中指定的日志信息 |
%p | 输出优先级,及 DEBUG、INFO 等 |
%n | 换行符(Windows平台的换行符为 “\n”,Unix 平台为 “\n”) |
%r | 输出自应用启动到输出该 log 信息耗费的毫秒数 |
%c | 输出打印语句所属的类的全名 |
%t | 输出产生该日志的线程全名 |
%d | 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日HH:mm:ss} |
%l | 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10) 小写 【不推荐】会影响性能 |
%F | 输出日志消息产生时所在的文件名称 【不推荐】会影响性能 |
%L | 输出代码中的行号,【不推荐】会影响性能 |
%% | 输出一个 “%” 字符 |
可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式。如:
形式 | 含义 |
---|---|
%5c | 输出category名称,最小宽度是5,category<5,默认的情况下右对齐 |
%-5c | 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格 |
%.5c | 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格 |
%20.30c | category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉 |
2.7 日志文件配置
log4j.rootLogger = trace,console
# 指定appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定格式化信息
log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
# 来源于SetFile方法
log4j.appender.file.file = ./log4j.log
log4j.appender.file.encoding = UTF-8
2.7.1 RollingFileAppender
log4j.rootLogger = trace,rollingFile
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.rollingFile.file = ./log4j.log
log4j.appender.rollingFile.encoding = UTF-8
log4j.appender.rollingFile.maxFileSize = 10KB
log4j.appender.rollingFile.maxBackupIndex = 1
2.7.2 DailyRollingFileAppender
log4j.rootLogger = trace,dailyRollingFile
# 按照文件的大小进行拆分
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.dailyRollingFile.file = ./log4j.log
log4j.appender.dailyRollingFile.encoding = UTF-8
# 指定日期拆分规则
log4j.appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
2.7.3 JDBCAppender
log4j.rootLogger = trace,sql
# 按照数据库级别进行日志记录
log4j.appender.sql = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.sql.layout = org.apache.log4j.PatternLayout
log4j.appender.sql.Driver = com.mysql.jdbc.Driver
log4j.appender.sql.URL = jdbc:mysql://localhost:3306/test
log4j.appender.sql.User = root
log4j.appender.sql.Password = root
log4j.appender.sql.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
);
2.8 自定义Logger
level会进行覆盖,appender会进行继承,也就是
dailyRollingFile
和console
会同时起作用。该**
包名
**下面的全部类都是使用这个这种类型
log4j.rootLogger = trace,dailyRollingFile
# 自定义logger
log4j.logger.cn.quguai = info,console
log4j.rootLogger = trace,dailyRollingFile
log4j.logger.cn.quguai = info,console
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.file.file = ./log4j.log
log4j.appender.file.encoding = UTF-8
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.rollingFile.file = ./log4j.log
log4j.appender.rollingFile.encoding = UTF-8
log4j.appender.rollingFile.maxFileSize = 1KB
log4j.appender.rollingFile.maxBackupIndex = 5
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.dailyRollingFile.file = ./log4j.log
log4j.appender.dailyRollingFile.encoding = UTF-8
log4j.appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
log4j.appender.sql = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.sql.layout = org.apache.log4j.PatternLayout
log4j.appender.sql.Driver = com.mysql.jdbc.Driver
log4j.appender.sql.URL = jdbc:mysql://localhost:3306/test
log4j.appender.sql.User = root
log4j.appender.sql.Password = root
log4j.appender.sql.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
3. JCL日志门面
3.1 JCL入门
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
默认使用JUL作为日志底层实现,当导入log4j的包的时候自动进行转换。
@Test
public void testQuick(){
Log log = LogFactory.getLog(JCLTest.class);
BasicConfigurator.configure();
log.fatal("s");
log.error("error");
log.warn("warning");
log.info("info");
log.debug("debug");
log.trace("trace");
}
3.2 JCL原理
private static final String[] classesToDiscover = new String[{
"org.apache.commons.logging.impl.Log4JLogger",
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
流程如下:
org.apache.commons.logging.LogFactory#getLog(java.lang.Class)
org.apache.commons.logging.impl.LogFactoryImpl#getInstance(java.lang.String)
org.apache.commons.logging.impl.LogFactoryImpl#newInstance
org.apache.commons.logging.impl.LogFactoryImpl#discoverLogImplementation
for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
}
4. Slf4J 技术
4.1 Slf4j 入门
使用 slf4j 内置的简单的实现
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--slf4j简单日志实现-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.26</version>
</dependency>
slf4j 相比于JCL 只提供了5个日志级别,去除了
fatal
public class Slf4jTest {
private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
@Test
public void testQuick(){
logger.error("error");
logger.warn("warning");
logger.info("info"); // slf4j-simple 默认日志级别 简单的日志框架的都是这个
logger.debug("debug"); // 复杂的日志框架都是这个,logback log4j
logger.trace("trace");
}
}
输出形式
logger.info("用户:{}{}", "李扬", 1);
try {
int i = 10 / 0;
} catch (Exception e) {
logger.error("出现异常", e);
}
4.2 日志绑定
本质上就是依靠
slf4j
进行日志门面的同意管理
蓝色
部分代表默认遵循了slf4j的规范可以直接进行使用青色
部分代表了适配层,需要引入桥接进行使用
slf4j-nop
代表了日志的开关,如果引入这个包,代表日志不在进行使用,全部禁用;- 适配层的包,默认会将上下两层的包全部导入,例如slf4j-log412.jar,默认依赖了slf4j和log4j;
- 直接实现的包,默认也直接依赖了slf4j;
推荐:直接引入处于第三层的包(红色边框)就可以完全导入所有依赖。
绑定流程:
- 添加slf4j-api的依赖
- 使用slf4j的API在项目中进行统一的日志记录
- 绑定具体的日志实现框架(其实默认就已经引入了slf4j的依赖)
- 绑定已经实现了slf4j的日志框架,直接添加对应依赖
- 绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
- slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)
4.3 绑定原理流程
org.slf4j.LoggerFactory#getLogger(java.lang.String)
org.slf4j.LoggerFactory#getILoggerFactory()
org.slf4j.LoggerFactory#performInitialization()
org.slf4j.LoggerFactory#bind()
org.slf4j.LoggerFactory#findPossibleStaticLoggerBinderPathSet() 其中的STATIC_LOGGER_BINDER_PATH=“org/slf4j/impl/
StaticLoggerBinder
.class”,只要实现这个方法就可以完成绑定操作。
4.4 桥接原理
桥接和绑定不同,绑定适用于前期的选型问题,确定下来就已经定死了,桥接适用于一些老代码的日志切换为Slf4j进行管理。不会改变原有的代码格式。桥接解决的是项目中日志的遗留问题,当系统中存在之前的日志API,可以通过桥接转换到slf4j的实现。
- 先去除之前老的日志框架的依赖
- 添加SLF4J提供的桥接组件
- 为项目添加SLF4J的具体实现
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.8</version>
</dependency>
注意问题:
jcl-over-slf4j.jar
和slf4j-jcl.jar
不能同时部署。前一个jar文件将导致JCL将日志系统的选择委托给 SLF4J,后一个jar文件将导致SLF4J将日志系统的选择委托给JCL,从而导致无限循环;log4j-over-slf4j.jar
和slf4j-log4j12.jar
不能同时出现;jul-to-slf4j.jar
和slf4j-jdk14.jar
不能同时出现;- 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是 Appender,Filter等对象,将无法产生效果。
5. logback
logback-classic 默认导入了 slf4j 以及 logback-core(都是一个人的嘛)
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.8</version>
</dependency>
默认日志级别debug (复杂日志框架(
log4j
logback
)都是debug,简单实现日志框架(jul
slf4j-sample
)都是info)
@Test
public void testQuick() {
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug"); // 默认级别
logger.trace("trace");
}
5.1 配置文件
可以通过 PropertiesTranslator 将log4j.properties 转化为xml形式
logback会依次读取以下类型配置文件:
- logback.groovy
- logback-test.xml
- logback.xml
- 如果均不存在会采用默认配置(
BasicConfigurator
,直接输出到控制台上)
logback组件之间的关系
- Logger: 日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也 可以定义日志类型、级别;
- Appender: 用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等;
- Layout: 负责把事件转换成字符串,格式化的日志信息的输出。在logback中Layout对象被封 装在encoder中。
5.1.1 配置文件详情
大写的简化形式都不推荐,影响性能问题
格式化 | 含义 | 备注 |
---|---|---|
%-5level = %p = %le | 日志级别 | |
%d{yyyy-MM-dd HH:mm:ss.SSS} | 日期 | |
%c{length} = %logger = %lo | 类的完整名称 | 后面数字代表简写的形式不代表长度 |
%thread= %t | 线程名称 | |
%r = %relative | 程序启动到创建日志记录的时间 | 单位是毫秒 |
%m = %msg | 信息 | |
%n | 换行 | |
%C = %class | 全限定类名 | 避免使用 |
%M | method | 避免使用 |
%L | 行号 | 避免使用 |
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--配置集中管理属性-->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--控制输出流对象,默认为System.out 变为红色字体-->
<target>System.err</target>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<root level="ALL">
<appender-ref ref="console"/>
</root>
</configuration>
5.1.2 文件输出
一般封装好的类都处于
classic
包下面,没有封装好的,进行二次开发的都处于core
包下面
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>${log_dir}/logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<root level="ALL">
<appender-ref ref="file"/>
</root>
</configuration>
5.1.3 自定义html输出
一般封装好的类都处于
classic
包下面,没有封装好的,进行二次开发的都处于core
包下面
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--配置集中管理属性-->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>${log_dir}/logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<appender name="html" class="ch.qos.logback.core.FileAppender"> <==classic包下面
<file>${log_dir}/logback.html</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <==core包下面
<layout class="ch.qos.logback.classic.html.HTMLLayout"> <==classic包下面
<pattern>%d{HH:mm:ss.SSS} %thread %-5level %c{36} - %msg%n</pattern>
</layout>
</encoder>
</appender>
<root level="ALL">
<appender-ref ref="file"/>
<appender-ref ref="html"/>
</root>
</configuration>
5.1.4 日志拆分和归档压缩
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<!--日志拆分和归档压缩-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_dir}/roll_logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--按照文件大小进行拆分-->
<maxFileSize>1mb</maxFileSize>
<!--按照时间进行拆分和归档压缩-->
<fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
</rollingPolicy>
</appender>
<root level="ALL">
<appender-ref ref="rollFile"/>
</root>
</configuration>
5.1.5 日志过滤器
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_dir}/roll_logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<maxFileSize>1mb</maxFileSize>
<fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
</rollingPolicy>
<!--过滤器-->
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <== 过滤器
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="ALL">
<appender-ref ref="rollFile"/>
</root>
</configuration>
5.1.6 异步日志记录
异步日志借助其他的appender完成异步任务,底层使用阻塞队列完成
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_dir}/roll_logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<maxFileSize>1mb</maxFileSize>
<fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
</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-ref ref="rollFile"/> <== 借助rollFile完成异步任务
</appender>
<root level="ALL">
<appender-ref ref="async"/>
</root>
</configuration>
5.1.7 自定义Logger
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_dir}/roll_logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<maxFileSize>1mb</maxFileSize>
<fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="ALL">
<appender-ref ref="rollFile"/>
</root>
<!--自定义logger additivity=false不存在父子继承关系-->
<logger name="cn.quguai" level="info" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>
**注意:**即使会输出root级别的文件,但是大小均为0kb。
5.2 logback-access的使用
logback-access模块与Servlet容器(如Tomcat和Jetty)集成,以提供HTTP访问日志功能。我们可以使 用logback-access模块来替换tomcat的访问日志。
-
将logback-access.jar与logback-core.jar复制到$TOMCAT_HOME/lib/目录下
-
修改$TOMCAT_HOME/conf/server.xml中的Host元素中添加:
<Valve className="ch.qos.logback.access.tomcat.LogbackValve" />
-
logback默认会在$TOMCAT_HOME/conf下查找文件 logback-access.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- always a good activate OnConsoleStatusListener --> <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/> <property name="LOG_DIR" value="${catalina.base}/logs"/> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_DIR}/access.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern> </rollingPolicy> <encoder> <!-- 访问日志的格式 --> <pattern>combined</pattern> <== logback集成的格式信息可以参考下方链接 </encoder> </appender> <appender-ref ref="FILE"/> </configuration>
6. Log4j2
6.1 快速入门
<!--日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!--日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
public class Log4j2Test {
private static final Logger logger = LogManager.getLogger(Log4j2Test.class);
@Test
public void testQuick(){
logger.fatal("fatal");
logger.error("error"); // 默认级别
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
实际开发当中依然使用slf4j加logback的组合方式进行
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.15.0</version>
</dependency>
6.2 配置文件
配置文件参数上和log4j形式上十分相同
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">./logs</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
</Console>
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n"/>
</File>
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n"/>
</RandomAccessFile>
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-dd-HH-mm}-%i.log">
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %msg%n"/>
<Policies>
<!--系统启动时,会自动进行触发,生成一个新的日志文件-->
<OnStartupTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
<!--同一个文件夹下,最大的文件个数-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<Loggers>
<!--默认使用rootLogger-->
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
6.3 异步日志(额外依赖)
- 异步日志形式
Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应 前面我们说的Appender组件和Logger组件。
注意:配置异步日志需要添加依赖
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
6.3.1 AsyncAppender方式
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">./logs</property>
</properties>
<Appenders>
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n"/>
</File>
<Async name="Async"> <==异步形式
<AppenderRef ref="file"/>
</Async>
</Appenders>
<Loggers>
<!--默认使用rootLogger-->
<Root level="error">
<AppenderRef ref="Async"/> <==异步形式
</Root>
</Loggers>
</Configuration>
6.3.2 AsyncLogger方式
AsyncLogger才是log4j2 的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的 更快。你可以有两种选择:全局异步和混合异步。
-
全局异步就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个
log4j2.component.properties
配置;Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
-
混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn" monitorInterval="5"> <properties> <property name="LOG_HOME">./logs</property> </properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/> </Console> <File name="file" fileName="${LOG_HOME}/myfile.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n"/> </File> </Appenders> <Loggers> <!--默认使用rootLogger--> <Root level="error"> <AppenderRef ref="Console"/> </Root> <!--自定义异步Logger对象 includeLocation:关闭日志行号信息 addivitity:是否进行继承--> <AsyncLogger name="cn.quguai" level="info" includeLocation="false" addivitity="false"> <AppenderRef ref="file"/> </AsyncLogger> </Loggers> </Configuration>
使用异步日志需要注意的问题:
- 如果使用异步日志,
AsyncAppender
、AsyncLogger
和全局日志
,不要同时出现。性能会和 AsyncAppender一致,降至最低。- 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。
7. SpringBoot 日志配置
默认使用slf4j + logback 进行实现
<dependency>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
7.1 快速入门
直接使用spring配置
logging.level.cn.quguai = debug
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c -%m%n
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c -%m%n
# 两者只能留下一个path默认文件名是spring.log
#logging.file.name=./logs/springboot.log
logging.file.path=./logs
logging.logback.rollingpolicy.file-name-pattern=${logging.file.path}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip
logging.logback.rollingpolicy.max-file-size=1KB
logging.logback.rollingpolicy.max-history=30
@SpringBootTest
class SpringBootLogApplicationTests {
private static final Logger logger = LoggerFactory.getLogger(SpringBootLogApplicationTests.class);
@Test
void contextLoads() {
logger.info("info"); // 默认级别
logger.debug("debug");
// 即使桥接器的形式使用log4j2的门面输出,默认还是会转为slf4j+logback
org.apache.logging.log4j.Logger logger = LogManager.getLogger(SpringBootLogApplicationTests.class);
logger.info("info log4j2");
logger.debug("debug log4j2");
}
}
7.2 指定配置
给类路径下放上每个日志框架自己的配置文件;SpringBoot就不使用默认配置的了
日志框架 | 配置文件 |
---|---|
Logback | logback-spring.xml, logback.xml |
Log4j2 | log4j2-spring.xml , log4j2.xml |
JUL | logging.properties |
7.3 使用SpringBoot解析日志配置
logback-spring.xml:由SpringBoot解析日志配置
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<springProfile name="default">
<pattern>${pattern}</pattern>
</springProfile>
<springProfile name="test">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} ==== %msg%n</pattern>
</springProfile>
</encoder>
spring.profiles.active=test
7.4 日志切换到log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion> <== 排除logging依赖:logback依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId> <== 引入log4j2的依赖,底层依然使用slf4j
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
7. 配置文件汇总
7.1 JUL配置文件
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
.level= ALL
# Self-Logger
cn.quguai.handlers = java.util.logging.FileHandler
cn.quguai.level = CONFIG
cn.quguai.useParentHandlers=false
java.util.logging.FileHandler.pattern = ./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.append = true
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
7.2 Log4j 配置文件
log4j.rootLogger = trace,dailyRollingFile
# ???logger
log4j.logger.cn.quguai = info,console
# ??appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# ??layout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# ???????
log4j.appender.console.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.file.file = ./log4j.log
log4j.appender.file.encoding = UTF-8
# ???????????
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.rollingFile.file = ./log4j.log
log4j.appender.rollingFile.encoding = UTF-8
# ???????????
log4j.appender.rollingFile.maxFileSize = 1KB
# ?????????
log4j.appender.rollingFile.maxBackupIndex = 5
# ???????????
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.conversionPattern = %r [%5t] %p %l - %m%n
log4j.appender.dailyRollingFile.file = ./log4j.log
log4j.appender.dailyRollingFile.encoding = UTF-8
# ????????
log4j.appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss
# ?????????????
log4j.appender.sql = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.sql.layout = org.apache.log4j.PatternLayout
log4j.appender.sql.Driver = com.mysql.jdbc.Driver
log4j.appender.sql.URL = jdbc:mysql://localhost:3306/test
log4j.appender.sql.User = root
log4j.appender.sql.Password = root
log4j.appender.sql.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
7.3 Logback 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--配置集中管理属性-->
<!-- 日志输出格式:
%-5level = %p = %le
%d{yyyy-MM-dd HH:mm:ss.SSS}日期
%c{length}类的完整名称 = %logger = %lo :后面数字代表简写的形式不代表长度
%C = %class :避免使用
%M为method :避免使用
%L为行号 :避免使用
%thread线程名称 = %t
%m或者%msg为信息
%n换行 -->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %c{36} - %msg%n"/>
<property name="log_dir" value="./logs"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--控制输出流对象,默认为System.out 变为红色字体-->
<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>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--html 格式输出-->
<appender name="html" class="ch.qos.logback.core.FileAppender">
<file>${log_dir}/logback.html</file>
<!--自定义 encoder -->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%d{HH:mm:ss.SSS} %thread %-5level %c{36} - %msg%n</pattern>
</layout>
</encoder>
</appender>
<!--日志拆分和归档压缩-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_dir}/roll_logback.log</file>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--按照文件大小进行拆分-->
<maxFileSize>1mb</maxFileSize>
<!--按照时间进行拆分和归档压缩-->
<fileNamePattern>${log_dir}/roll_logback.%d{yyyy-MM-dd-HH-mm-ss}.log%i.zip</fileNamePattern>
<!--只保留最近30天的-->
<maxHistory>30</maxHistory>
</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-ref ref="rollFile"/>
</appender>
<root level="ALL">
<appender-ref ref="console"/>
<appender-ref ref="async"/>
<appender-ref ref="html"/>
</root>
<!--自定义logger additivity是否存在父子继承关系-->
<logger name="cn.quguai" level="info" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>
7.4 Log4j2 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">./logs</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"/>
</Console>
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n"/>
</File>
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %m%n"/>
</RandomAccessFile>
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyyMM-dd-HH-mm}-%i.log">
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l%c{36} - %msg%n"/>
<Policies>
<!--系统启动时,会自动进行触发,生成一个新的日志文件-->
<OnStartupTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
<!--同一个文件夹下,最大的文件个数-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!--使用异步Appender 等同于Logback-->
<!--<Async name="Async">
<AppenderRef ref="file"/>
</Async>-->
</Appenders>
<Loggers>
<!--默认使用rootLogger-->
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
<!--自定义异步Logger对象 includeLocation:关闭日志行号信息 addivitity:是否进行继承-->
<AsyncLogger name="cn.quguai" level="info" includeLocation="false" addivitity="false">
<AppenderRef ref="file"/>
</AsyncLogger>
</Loggers>
</Configuration>
使用异步日志需要注意的问题:
- 如果使用异步日志,
AsyncAppender
、AsyncLogger
和全局日志
,不要同时出现。性能会和AsyncAppender
一致,降至最低。- 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。