Log4j其实更多的是一个工具,主要用于我们程序运行时需要记录日志的时候,这些日志可以用来进行数据分析和问题定位。
1. 准备工作
Log4j的官网是:http://logging.apache.org/log4j/2.x/
Maven依赖项添加如下:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
2.使用
Log4j最重要的使用是配置文件,默认的文件名是log4j.properties和log4j.xml。首先我们通过一个最简单的例子进行讲解。
2.1 配置文件
这里我们先使用properties文件进行讲解:
#首先设置rootLogger,即输出级别过滤和输出目的地
log4j.rootLogger=debug,myappender
#输出到控制台
log4j.appender.myappender=org.apache.log4j.ConsoleAppender
#样式为TTCCLayout
log4j.appender.myappender.layout=org.apache.log4j.TTCCLayout
可以看到,我们需要设置的属性包括:输出级别、输出目的地、输出格式。
2.2 调用代码
调用代码非常简单,如下:
public class LogUtil {
public static void debug(Object sender, Object msg) {
Logger logger = Logger.getLogger(sender.getClass());
logger.debug(msg);
}
public static void info(Object sender, Object msg) {
Logger logger = Logger.getLogger(sender.getClass());
logger.info(msg);
}
public static void warn(Object sender, Object msg) {
Logger logger = Logger.getLogger(sender.getClass());
logger.warn(msg);
}
public static void error(Object sender, Object msg) {
Logger logger = Logger.getLogger(sender.getClass());
logger.error(msg);
}
public static void fatal(Object sender, Object msg) {
Logger logger = Logger.getLogger(sender.getClass());
logger.fatal(msg);
}
}
这里我们提供调用者的参数包括:调用者、输出消息。
其中调用者用于给Logger类使用,这样可以在后面的输出格式中输出调用的类名等信息,消息及我们需要输出的内容。
这样,下面到了我们来详细讲解配置文件的时候了。
3. 配置文件详解
相对来说我个人觉得xml文件格式更好看一些,且xml能够提供的配置项更多,所以这里我将使用xml作为我们讲解的配置文件。
3.1 基本概念
之前我们讲过我们需要设置输出目的地、输出格式和输出级别。下面我们分别详细来讲。
3.1.1 输出目的地
输出目的地有好多种类型,后面我们会用例子来讲解,这里我们需要知道的主要类型有:
- org.apache.log4j.RollingFileAppender(滚动文件,自动记录最新日志)
- org.apache.log4j.ConsoleAppender (控制台)
- org.apache.log4j.FileAppender (文件)
- org.apache.log4j.DailyRollingFileAppender (每天产生一个日志文件)
- org.apache.log4j.WriterAppender (将日志信息以流格式发送到任意指定的地方)
3.1.2 输出格式
输出格式主要包括以下格式,我们也会在后面的例子中详细示例如何使用:
- org.apache.log4j.HTMLLayout(以HTML表格形式布局)
- org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
- org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
- org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
3.1.3 输出级别
输出级别主要包括:OFF、DEBUG、INFO、WARN、ERROR、FATAL,依次递减。也就是说,如果你设置了ERROR级别,那么WARN之前的级别均不可见,只会输出ERROR和FATAL。
3.2 ConsoleAppender
控制台输出+最简单的SimpleLayout例子:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="myConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.SimpleLayout" />
<param name="ImmediateFlush" value="true" />
<param name="Target" value="System.out" />
<param name="Threshold" value="INFO" />
<!--过滤器设置输出的级别 -->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="levelMin" value="debug" />
<param name="levelMax" value="warn" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<!-- 指定logger的设置,additivity指示是否遵循缺省的继承机制 -->
<logger name="com.freesoft.testAppender" additivity="false">
<priority value="info" />
<appender-ref ref="myConsoleAppender" />
</logger>
<!-- 根logger的设置 -->
<root>
<!-- root级别输出设置 -->
<priority value="debug" />
<!-- 目的地 -->
<appender-ref ref="com.freesoft.testAppender" />
</root>
</log4j:configuration>
注意,理解顺序上应该首先看root的设置,root中引用到了com.freesoft.testAppender,testAppender又引用了myConsoleAppender,myConsoleAppender中进行了一系列详细的设置。后面的例子中我仅针对每一个Appender来给出设置的例子。
3.3 FileAppender
将输出内容写入到文件中。
<appender name="myFileAppender" class="org.apache.log4j.FileAppender">
<param name="ImmediateFlush" value="true" />
<param name="Threshold" value="INFO" />
<layout class="org.apache.log4j.TTCCLayout" />
<param name="Append" value="true" />
<param name="File" value="D:/tmp/log4j.log" />
</appender>
3.3 RollingFileAppender
将输出内容写入到不断增长的文件中,如果文件内容增长到一定大小,将会自动写入另一文件。
<!-- Threshold=WARN:指定日志信息的最低输出级别,默认为DEBUG。 -->
<!-- ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true。 -->
<appender name="myRollingFileAppender" class="org.apache.log4j.RollingFileAppender">
<layout class="org.apache.log4j.HTMLLayout">
<param name="LocationInfo" value="true" />
<param name="Title" value="My Logging" />
</layout>
<param name="File" value="D:/tmp/rolling.html" />
<param name="Append" value="true" />
<!-- 后缀可以是KB, MB 或者GB。在日志文件到达该大小时,将会自动滚动,即将原来的内容移到logging.log4j.1文件中。 -->
<param name="MaxFileSize" value="10kb" />
<!-- 指定可以产生的滚动文件的最大数,例如,设为2则可以产生logging.log4j.1,logging.log4j.2两个滚动文件和一个logging.log4j文件。 -->
<param name="MaxBackupIndex" value="10" />
</appender>
3.4 DailyRollingFileAppender
根据设定的时间值,将内容分别写入到不同文件中。这个在我们开发的Web Application中经常用到,我们的日志一般都是按照每天固定保存的,便于问题分析和每天凌晨时分的统计分析。
<!-- Threshold=WARN:指定日志信息的最低输出级别,默认为DEBUG。 -->
<!-- ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true。 -->
<appender name="myDailyRollingFileAppender" class="org.apache.log4j.DailyRollingFileAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="[%d{yy-MM-dd HH:mm:ss,SSS\} %-5p] [%t] %c %n -%l - %m%n" />
</layout>
<param name="File" value="D:/tmp/rolling.log" />
<param name="Append" value="true" />
<!-- 今天的日志文件名为logging.log4j,前一个月的日志文件名为logging.log4j.yyyy-MM。 -->
<!-- 可以指定按周、天、时、分等来滚动日志文件,对应的格式如下: -->
<!-- 1)'.'yyyy-MM:每月 -->
<!-- 2)'.'yyyy-ww:每周 -->
<!-- 3)'.'yyyy-MM-dd:每天 -->
<!-- 4)'.'yyyy-MM-dd-a:每天两次 -->
<!-- 5)'.'yyyy-MM-dd-HH:每小时 -->
<!-- 6)'.'yyyy-MM-dd-HH-mm:每分钟 -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
</appender>
注意这里我们使用到了PatternLayout,这个也是我们最常用的,其中的表达式中的意思分别代表:
- %m 输出代码中指定的消息
- %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
- %r 输出自应用启动到输出该log信息耗费的毫秒数
- %c 输出所属的类目,通常就是所在类的全名
- %t 输出产生该日志事件的线程名
- %l 输出产生该日志事件的代码位置
- %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
- %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式。比如:%d{yy-MM-dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
- %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。相当于%c.%M(%F:%L)的组合
- %x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
- %M:输出产生日志信息的方法名。
- %F:输出日志消息产生时所在的文件名称。
- %L::输出代码中的行号。
- %%:输出一个"%"字符。
- c:指定输出category的名称,最小的长度是20,如果category的名称长度小于20的话,默认的情况下右对齐。
- %-20c:"-"号表示左对齐。
- %.30c:指定输出category的名称,最大的长度是30,如果category的名称长度大于30的话,就会将左边多出的字符截掉,但小于30的话也不会补空格。
3.5 RemoteAppender
这个配置用于将服务器上生成的log输出到另一台log服务器。
3.5.1 待输出log服务器配置
如果我们一台Web Application服务器需要将日志输出到另一台服务器,可以这样配置(我这里的试验是将其输出到本地):
<!-- 可选参数Threshold -->
<appender name="remoteAppender" class="org.apache.log4j.net.SocketAppender">
<!-- 这是远程log server -->
<param name="remoteHost" value="localhost" />
<!-- 这是远程log server port -->
<param name="port" value="4560" />
<param name="ReconnectionDelay" value="1000" />
<param name="LocationInfo" value="true" />
</appender>
同样还需要对另外一台接收日志的服务器进行配置,这里我使用了properties文件:
# 这里配置的是SocketServer这个Appender的配置,其输出的log会写入到log4jserver.log中
log4j.logger.org.apache.log4j.net.SocketServer=DEBUG,serverFile
log4j.additivity.org.apache.log4j.net.SocketServer=false
log4j.appender.serverFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.serverFile.DatePattern='.'yyyy-MM-dd-HH
log4j.appender.serverFile.encoding=UTF-8
log4j.appender.serverFile.File=D:/tmp/log4jserver.log
log4j.appender.serverFile.layout=org.apache.log4j.PatternLayout
log4j.appender.serverFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss},%m %n
# 这里是log4j的root配置,用于接收客户端传递的log内容
log4j.rootCategory=INFO, globalfile
log4j.appender.globalfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.globalfile.DatePattern='.'yyyy-MM-dd-HH
log4j.appender.globalfile.encoding=UTF-8
log4j.appender.globalfile.File=D:/tmp/global.log
log4j.appender.globalfile.layout=org.apache.log4j.PatternLayout
log4j.appender.globalfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss},%m %n
然后在服务端运行以下命令启动服务端,再从客户端运行测试输出log即可:
java -cp log4j-1.2.17.jar org.apache.log4j.net.SocketServer 4560 D:/tmp/log4j-server.properties D:/tmp/
注意:这里其余的几个参数都容易理解,D:/tmp/lg4j-server.properties是我们上面的配置文件,D:/tmp这个参数是用来在你需要将对应每个ip来的log都分别输出到不同文件的时候去用的,你可以在D:/tmp目录下放几个文件,基本的配置使用generic.lcf,文件内容如下:
log4j.rootCategory=,A4
log4j.appender.A4=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A4.file=127.0.0.1.log
log4j.appender.A4.DatePattern='.'yyyyMMdd
log4j.appender.A4.layout=org.apache.log4j.PatternLayout
log4j.appender.A4.layout.ConversionPattern=/n/n[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
如果你需要对192.168.1.1的这个ip来的log存储到另一个文件,则需要配置内容如上的一个192.168.1.1.lcf文件,并且需要修改下org.apache.log4j.net.SocketServer代码中的一个小bug。
LoggerRepository configureHierarchy(InetAddress inetAddress)
{
cat.info("Locating configuration file for " + inetAddress);
String s = inetAddress.toString();
int i = s.indexOf("/");
if (i == -1) {
cat.warn("Could not parse the inetAddress [" + inetAddress + "]. Using default hierarchy.");
return genericHierarchy();
}
// 将下面一行内容注释
// String key = s.substring(0,i);
String key = s.substring(i+1);
File configFile = new File(this.dir, key + CONFIG_FILE_EXT);
if (configFile.exists()) {
Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
this.hierarchyMap.put(inetAddress, h);
new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);
return h;
}
cat.warn("Could not find config file [" + configFile + "].");
return genericHierarchy();
}
否则会报找不到.lcf文件的异常。
3.6 SMTPAppender
这个配置可以将生成的log以email形式发送。
<appender name="mySMTPAppender" class="org.apache.log4j.net.SMTPAppender">
<param name="Threshold" value="INFO" />
<param name="BufferSize" value="128" />
<!-- 注意不要使用smtp.qq.com,这个地址是需要ssl链接的 -->
<param name="SMTPHost" value="smtp.exmail.qq.com" />
<param name="SMTPUsername" value="xxx@qq.com" />
<param name="SMTPPassword" value="xxx" />
<param name="From" value="xxx@qq.com" />
<param name="To" value="xxx@qq.com" />
<param name="Subject" value="测试邮件发送log" />
<param name="LocationInfo" value="true" />
<param name="SMTPDebug" value="true" />
<layout class="org.apache.log4j.SimpleLayout" />
</appender>
3.7 JDBCAppender
这里仅演示写入MySQL数据库的例子:
<appender name="myJDBCAppender" class="org.apache.log4j.jdbc.JDBCAppender">
<param name="URL" value="jdbc:mysql://localhost:3306/log4j" />
<param name="driver" value="com.mysql.jdbc.Driver" />
<param name="user" value="root" />
<param name="password" value="root" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="INSERT INTO log4j(stamp,thread, info_level,class,message) VALUES ('%d', '%t', '%p', '%c', '%m')" />
</layout>
</appender>