官网地址 | 官方文档

1
2
3
4
5
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>

日志级别

  • fatal
    • 指出每个严重的错误事件将会导致应用程序的退出。
  • error
    • 虽然发生错误事件,但仍然不影响系统的继续运行。
  • warn
    • 表明会出现潜在的错误情形。
  • info
    • 一般和在粗粒度级别上,强调应用程序的运行全程。
  • debug
    • 一般用于细粒度级别上,对调试应用程序非常有帮助。默认级别
  • trace
    • 是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。

      推荐:实际开发中只会使用到最中间的四个步骤。

1
2
3
4
5
6
7
8
9
10
11
12
@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");
}

Log4j 组件

Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。
其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件 等);Layout 控制日志信息的输出格式。

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 类的别名。

Appenders

Appender 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j 常用的输出目的地有以下几种:

  • ConsoleAppender
    • 将日志输出到控制台
  • FileAppender
    • 将日志输出到文件中
  • DailyRollingFileAppender
    • 将日志输出到一个日志文件,并且每天输出到一个新的文件
  • RollingFileAppender
    • 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大 小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件
  • JDBCAppender
    • 把日志信息保存到数据库中

Layouts

布局器 Layouts用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。
Log4j常用 的Layouts:

  • HTMLLayout
    • 格式化日志输出为HTML表格形式
  • SimpleLayout
    • 简单的日志输出格式化,打印的日志格式为(info - message)
  • PatternLayout
    • 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式, 就是用默认的转换格式

配置文件详情

配置文件的后缀名称全部来源于对应的set方法注入,例如conversionPattern 使用SetConversionPattern方法完成

1
2
3
4
5
6
7
8
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

配置文件源码

支持的配置文件

1
2
3
4
5
6
7
8
9
10
11
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 ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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";
}

rootLogger 解析原理

rootLogger 的配置源码 org.apache.log4j.PropertyConfigurator#parseCategory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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);
}
}
}
}

内部日志记录

1
2
3
4
5
6
7
public class LogLog {
public static void debug(String msg) {
if (debugEnabled && !quietMode) {
System.out.println("log4j: " + msg);
}
}
}

可以使用 LogLog.setInternalDebugging(true) 打开开关,这是LogLog的内部静态方法。

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字符,就从左边交远销出的字符截掉

日志文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

RollingFileAppender

1
2
3
4
5
6
7
8
9
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

DailyRollingFileAppender

1
2
3
4
5
6
7
8
9
10
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

JDBCAppender

1
2
3
4
5
6
7
8
9
10
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')
1
2
3
4
5
6
7
8
9
10
11
12
13
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`)
);

自定义Logger

level会进行覆盖,appender会进行继承,也就是dailyRollingFile和console会同时起作用。
包名下面的全部类都是使用这个这种类型

1
2
3
4
log4j.rootLogger = trace,dailyRollingFile

# 自定义logger
log4j.logger.cn.quguai = info,console
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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')