Java进阶(五十二)利用LOG4J生成服务日志
前言
由于论文写作需求,需要进行流程挖掘。前提是需要有真实的事件日志数据。真实的事件日志数据可以用来发现、监控和提升业务流程。
为了获得真实的事件日志数据,决定采用Log4J日志生成工具。首先,对工具有个大致的了解:日志是应用软件中不可缺少的部分,Apache的开源项目Log4j是一个功能强大的日志组件,提供方便的日志记录。
Log4j下载
在apache网站:http://logging.apache.org/log4j/1.2/download.html
可以免费下载到Log4j最新版本的软件包。
Log4j使用
Log4j的包下载完成后,解压,将其中打包好的的log4j-1.x.x.jar导入你的工程lib中。
Log4j之所以受欢迎的原因之一是它的灵活性。Log4j提供了灵活的配置方法,默认是调用BasicConfigurator.configure()来进行配置,但如果只是简单的调用BasicConfigurator.configure()来进行配置工作,那么所有的配置都是固定的,不方便以后修改配置。另一种是动态配置,Log4j提供了PropertyConfigurator.configure(……)来动态配置,参数可以是一个properties文件所在路径的String对象,可以是一个properties文件所在路径的URL对象,也可以是一个properties对象。如果要用XML文件来配置信息,则可用类型的DOMConfigurator()函数来从一个XML文件中加载配置信息。这种方式更方便修改配置。
package http; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.DOMConfigurator; public class Log4jDemo { static Logger log = Logger.getLogger(Log4jDemo.class.getClass()); public static void main(String[] args) { BasicConfigurator.configure();//默认配置 PropertyConfigurator.configure("c:/log4j.properties"); //动态配置,参数可以是一个properties文件所在路径的String对象 //可以是一个properties文件所在路径的URL对象,也可以是一个properties对象 //PropertyConfigurator.configure()的参数还可以是XML、Properties对象 DOMConfigurator.configure("c:/log4j.xml");//XML配置文件 //下面就可使用log4j log.info("info"); log.debug("debug"); log.error("error"); log.warn("warn"); } }
J2EE应用log4j
在J2EE应用使用Log4j,必须先在启动服务时加载Log4j的配置文件进行初始化,可以在web.xml中进行。
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class J2eeLog4jDemo extends HttpServlet { public void destroy() { super.destroy(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } public void init() throws ServletException { //通过web.xml来动态取得配置文件 String prefix = getServletContext().getRealPath("/"); String file = getInitParameter("log4j"); //如果没有给出相应的配置文件,则不进行初始化 if(file != null) { PropertyConfigurator.configure(prefix+file); } } }
Web.xml 代码
<servlet> <servlet-name>j2eelog4jdemo<servlet-name> <servlet-class>J2eeLog4jDemo<servlet-class> <init-param> <param-name>log4j<param-name> <param-value>log4j.properties<param-value> </init-param> <load-on-startup>1<load-on-startup> //设为1时Web容器启动即进行加载 </servlet>
Log4j配置内容
Log4j配置内容主要是依据配置文件log4j.properties:
#指定根Logger,及日志输出级别
#大于等于该级别的日志将被输出( DEBUG < INFO < WARN < ERROR < FATAL ),设为OFF可以关闭日志,指定日志信息输出到哪个地方。可以同时指定多个输出目的地。
log4j.rootLogger=DEBUG, A1,A2
#指定log输出目的,这里设为输出日志到指定目录的文件my.log中
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.File=\\logs\\my.log #当前根目录下
#指定日志信息的格式
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%r %d{yyyy-MM-dd HH:mm:ss} %c %p -%m%n
#把A2输出到控制台
log4j.appender.A2=org.apache.log4j.ConsoleAppender
log4j.appender.A2.layout=org.apache.log4j.SimpleLayout
#还可以单独指定输出某个包的日志级别
#log4j.logger.com.study.HelloLog4j=INFO
1、配置根Logger
其语法为: log4j.rootLogger = [ level ] , appenderName, appenderName2
level:日志的级别,指定这条日志信息的重要性。分为ALL < DEBUG < INFO < WARN < ERROR < FATAL。一般常用的为 DEBUG , INFO ,WARN ,ERROR四种,分别对应Logger类的四种方法
debug(Object message ) ;
info(Object message ) ;
warn(Object message ) ;
error(Object message ) ;
如果设置级别为INFO,则优先级大于等于INFO级别(如:INFO、WARN、ERROR)的日志信息将可以被输出,小于该级别的如:DEBUG将不会被输出
appenderName :就是指定日志信息输出目的地,比如(打印到控制台,输出到文件等)。同一条日志信息可以配置多个输出目的地。
2、配置log输出目的地
Log4j提供以下几种:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
3、log信息的格式
org.apache.log4j.HTMLLayout(HTML表格形式)
org.apache.log4j.SimpleLayout(简单格式的日志,只包括日志信息的级别和指定的信息字符串 ,如:DEBUG - Hello)
org.apache.log4j.TTCCLayout(日志的格式包括日志产生的时间、线程、类别等等信息)
org.apache.log4j.PatternLayout(灵活地自定义日志格式)
当使用org.apache.log4j.PatternLayout来自定义信息格式时,可以使用
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p -%m%n 来格式化信息
%c 输出所属类的全名,可写为 %c{Num} ,Num类名输出的范围 如:"com.sun.aaa.classB", %C{2}将使日志输出输出范围为:aaa.classB
%d 输出日志时间其格式为 可指定格式 如 %d{HH:mm:ss}等
%l 输出日志事件发生位置,包括类目名、发生线程,在代码中的行数
%n 换行符
%m 输出代码指定信息,如info(“message”),输出message
%p 输出日志的优先级,即 FATAL ,ERROR 等
%r 输出从启动到显示该条日志信息所耗费的时间(毫秒数)
%t 输出产生该日志事件的线程名
更详细参数信息可参看类org.apache.log4j.PatternLayout的API doc文档
后知后觉
经过查看,才发现原来项目中已经使用了log4j,自己需要给其再加上时间戳。不过这些日志还得经过后期的处理,因为现在的信息太琐碎了。
以下为某一交易的日志数据:
[21:20:52][weiming.lmapp.servlet.DoUpdate] pkgtype=json
[21:20:52][weiming.lmapp.servlet.DoUpdate] content-length=106
[21:20:52][weiming.lmapp.servlet.RequestMessage] receiver:{"billtype":"1236","version":"1.0","fore_time":"2015-12-09 21:20:52","uid":"18853883588","tx_code":"9010"}
[21:20:52][weiming.lmapp.trans.Transaction]
------------Order_Query_9010 debug info-------------
请求数据包信息:{"billtype":"1236","version":"1.0","fore_time":"2015-12-09 21:20:52","uid":"18853883588","tx_code":"9010"}
[21:20:52][weiming.lmapp.trans.Transaction] stat:[1]
[21:20:52][weiming.lmapp.trans.Transaction] stat:[2]
[21:20:52][weiming.lmapp.trans.Transaction] stat:[3]
[21:20:52][weiming.lmapp.trans.Transaction] stat:[6]
[21:20:52][weiming.lmapp.trans.Transaction] 查询条件:[buy_uid='U18853883588' AND ( stat='1' OR stat='2' OR stat='3' OR stat='6' )ORDER BY tx_time DESC]
[21:20:52][weiming.database.lmapp.MyDatabase] sqlstr=select billid,tx_time,buy_uid,merch_uid,msg,old_amt,amt,receiver,tel,buy_addr,stat,pay_type,pl_stat from lm_bill where buy_uid='U18853883588' AND ( stat='1' OR stat='2' OR stat='3' OR stat='6' )ORDER BY tx_time DESC limit 0,1000
[21:20:52][weiming.lmapp.trans.Transaction] count:[1]
[21:20:52][weiming.database.lmapp.MyDatabase]sqlstr=select billid,cnt,medid,medname,medcnt,medprice,medamt,cfid from lm_bill_dtl where billid='LM2015120935888906' limit 0,1000
[21:20:52][weiming.lmapp.servlet.DoUpdate] Transaction 9010 success
[21:20:52][weiming.lmapp.servlet.RequestMessage] OUTSTR:
{"cnt":1,"data":[{"med_amt":8,"amt":8,"med_cnt":1,"tx_time":"2015-12-09 21:20:49","medname":"感冒灵颗粒","medid":"TZ20055267","stat":"1","billid":"LM2015120935888906"}],"result":"0000","errtext":""}
经过观察,发现以上日志数据并不完全满足自己的需求,需做进一步的改进。改进后产生的日志信息如下:
[15/12/09 21:50:36:332][weiming.lmapp.servlet.DoUpdate] pkgtype=json
[15/12/09 21:50:36:333][weiming.lmapp.servlet.DoUpdate] content-length=107
[15/12/09 21:50:36:333][weiming.lmapp.servlet.RequestMessage] receiver:{"billtype":"12369","version":"1.0","fore_time":"2015-12-09 21:50:36","uid":"18853883588","tx_code":"9010"}
[15/12/09 21:50:36:333][weiming.lmapp.trans.Transaction]
------------Order_Query_9010 debug info-------------
请求数据包信息:{"billtype":"12369","version":"1.0","fore_time":"2015-12-09 21:50:36","uid":"18853883588","tx_code":"9010"}
[15/12/09 21:50:36:333][weiming.lmapp.trans.Transaction] stat:[1]
[15/12/09 21:50:36:334][weiming.lmapp.trans.Transaction] stat:[2]
[15/12/09 21:50:36:334][weiming.lmapp.trans.Transaction] stat:[3]
[15/12/09 21:50:36:334][weiming.lmapp.trans.Transaction] stat:[6]
[15/12/09 21:50:36:334][weiming.lmapp.trans.Transaction] stat:[9]
[15/12/09 21:50:36:334][weiming.lmapp.trans.Transaction] 查询条件:[buy_uid='U18853883588' AND ( stat='1' OR stat='2' OR stat='3' OR stat='6' OR stat='9' )ORDER BY tx_time DESC]
[15/12/09 21:50:36:334][weiming.database.lmapp.MyDatabase] sqlstr=select billid,tx_time,buy_uid,merch_uid,msg,old_amt,amt,receiver,tel,buy_addr,stat,pay_type,pl_stat from lm_bill where buy_uid='U18853883588' AND ( stat='1' OR stat='2' OR stat='3' OR stat='6' OR stat='9' )ORDER BY tx_time DESC limit 0,1000
[15/12/09 21:50:36:335][weiming.lmapp.trans.Transaction] count:[1]
[15/12/09 21:50:36:336][weiming.database.lmapp.MyDatabase] sqlstr=select billid,cnt,medid,medname,medcnt,medprice,medamt,cfid from lm_bill_dtl where billid='LM2015120935888906' limit 0,1000
[15/12/09 21:50:36:337][weiming.lmapp.servlet.DoUpdate] Transaction 9010 success
[15/12/09 21:50:36:338][weiming.lmapp.servlet.RequestMessage] OUTSTR:
{"cnt":1,"data":[{"med_amt":8,"amt":8,"med_cnt":1,"tx_time":"2015-12-09 21:20:49","medname":"感冒灵颗粒","medid":"TZ20055267","stat":"9","billid":"LM2015120935888906"}],"result":"0000","errtext":""}
通过观察可以发现,以上日志主要包含的信息有:服务请求者、服务时间段、服务内容、服务结果
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
学术需要,现在自己需要实现按日期生成日志文档,log4j配置如下:
log4j.appender.appender2=org.apache.log4j.DailyRollingFileAppender log4j.appender.appender2.DatePattern=yyyy-MM-dd'.log' log4j.appender.appender2.layout=org.apache.log4j.PatternLayout log4j.appender.appender2.layout.ConversionPattern=[%d{yy/MM/dd HH:mm:ss:SSS}][%c] %m%n log4j.appender.appender2.File=e\:\\MyEclipse10\\logs\\log_
当天生成的日志,保存在项目的根路径下的","tx_code":""}
[15/12/09 22:02:21:532][weiming.lmapp.trans.Transaction]
------------Order_Query_9010 debug info-----------
请求数据包信息:
{"billtype":"1236","version":"1.0","fore_time":"2015-12-09 22:02:22","uid":"18853883588","tx_code":"9010"}
[15/12/09 22:02:21:547][weiming.lmapp.trans.Transaction] stat:[1]
[15/12/09 22:02:21:547][weiming.lmapp.trans.Transaction] stat:[2]
[15/12/09 22:02:21:547][weiming.lmapp.trans.Transaction] stat:[3]
[15/12/09 22:02:21:547][weiming.lmapp.trans.Transaction] stat:[6]
[15/12/09 22:02:21:547][weiming.lmapp.trans.Transaction] 查询条件:
[buy_uid='U18853883588' AND ( stat='1' OR stat='2' OR stat='3' OR stat='6' )ORDER BY tx_time DESC]
[15/12/09 22:02:21:547][weiming.database.lmapp.MyDatabase] sqlstr=
selectbillid,tx_time,buy_uid,merch_uid,msg,old_amt,amt,receiver,tel,buy_addr,stat,pay_type,pl_stat from lm_bill where buy_uid='U18853883588' AND ( stat='1' OR stat='2' OR stat='3' OR stat='6' )ORDER BY tx_time DESC limit 0,1000
[15/12/09 22:02:21:595][weiming.lmapp.trans.Transaction] count:[0]
[15/12/09 22:02:21:595][weiming.lmapp.trans.Transaction]
[15/12/0922:02:21:595][weiming.lmapp.servlet.DoUpdate]Transaction 9010 error
[15/12/09 22:02:21:595][weiming.lmapp.servlet.RequestMessage] OUTSTR:
{"cnt":0,"result":"9999","errtext":"无订单信息"}
获取到了数据源信息,自己就需要做进一步的处理了。