由於最近正在擴展衛生局考務系統,由於上一次使用過一次該系統后,發現很多考生最后出現說已打印報名表但卻在數據庫中找不到她的報名記錄;
因此為了以后有依據,將所有用戶的操作日志文件寫入數據供管理員查詢成為了這次擴張項目的一個內容;
這里我決定使用的log4j日志文件,在多次的使用中感覺這個很不錯;
首先我們要使用log4j日志文件時,我們需將兩個必須的包放入lib目錄下:log4j.XX.jar和commons-logging.XX.jar;
然后在classpath目錄下(IDE中即為項目下的src目錄下)新建一個日志文件,統一命名為:log4j.properties;
1.需求一:只需要滿足存儲數據庫:
a.以下我們一ms sql 2000為例:首先在數據庫中建一個用來存儲日志的數據庫命名為 operate_log;字段如下:
b.數據庫成功建立后,就可以去配置日志文件log4j.properties,代碼如下:
log4j.properties log4j.rootLogger=INFO,db ######################## # JDBC Appender ####################### #log4j.logger.business=INFO,db #log4j.appender.db=com.neam.commons.MyJDBCAppender log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender log4j.appender.db.BufferSize=1 log4j.appender.db.driver=net.sourceforge.jtds.jdbc.Driver log4j.appender.db.URL=jdbc:jtds:sqlserver://localhost:1433;DatabaseName=infor_manage #enter log4j.appender.db.user=sa log4j.appender.db.password=123 log4j.appender.db.sql=insert into operate_log(class,method,createtime,loglevel,logmsg,user_id,user_type) values ('%C','%M','%d{yyyy-MM-dd HH\:mm\:ss}','%p','%m','1',1) log4j.appender.db.layout=org.apache.log4j.PatternLayout
上面的配置就是最精簡的將日志內容直接存儲進入數據庫
下面來稍微解釋:
log4j.rootLogger=INFO,db語法為:
##log4j.rootLogger = [ level ] , appenderName1, appenderName2, …
##level : 是日志記錄的優先級,分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定義的級別。Log4j建議只使用四個級別,優先級從高到低分別是ERROR、##WARN、INFO、DEBUG。通過在這里定義的級別,您可以控制到應用程序中相應級別的日志信息的開關。比如在這里定義了INFO級別,則應用程序中所有DEBUG級別的日志##信息將不被打印出來。
##appenderName:就是指定日志信息輸出到哪個地方。您可以同時指定多個輸出目的地。
##例如:log4j.rootLogger=info,A1,B2,C3 配置了3個輸出地方,這個名字可以任意(如上面的db),但必須與我們在后面進行的設置名字對應;
然后下面就是進行數據庫連接的配置,log4j是使用jdbc進行連接的,該封轉的類就是log4j包下的 org.apache.log4j.jdbc.JDBCAppender,大家對jdbc了解的話上面的內容應該是很簡單的;
這里要注意的就是:1.記得把數據庫連接的相關包放到lib目錄下,2.在寫連接數據庫的信息時如user等注意后面不要有空格,否則就不能連上數據庫
c.日志文件配置完成后,我們就可以進行測試了,我們可以隨便在后台寫一個類:
package xidian.sl.action.admin; import org.apache.log4j.Logger; import com.opensymphony.xwork2.ActionSupport; public class HelloWorld extends ActionSupport{ /** * */ private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(HelloWorld.class);//日志文件 public static void main(String[] args) { log.error("訪問了HelloWorld"); log.warn("訪問了HelloWorld"); log.info("訪問了HelloWorld"); log.debug("訪問了HelloWorld"); } }
然后右鍵運行,如果沒有報錯的話應該是成功了,可以去數據庫看看:
可以看到日志信息已經進行了存儲,但發現只有三條,少了debug,對了,由於我們進行了日志優先級的配置:log4j.rootLogger=INFO,db,只有debug級別就不能進行打印了;
到這里我們可以說基本成功了,但還遠遠不能滿足我的需求:
我們發現數據庫中出現了很多的日志信息,這個日志信息應該是啟動等,從系統文件(spring等)中打印的,但這個其實不是我們需要的,或者說我們需要將其分開:
我們重新進行日志文件的配置
log4j.properties log4j.rootLogger=INFO,stdout log4j.logger.xidian=INFO,db log4j.logger.org=WARN, A1 log4j.logger.com =WARN, A2 #stdout\u5e94\u7528\u4e8e\u63a7\u5236\u53f0 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%d %5p (%c\:%L) - %m%n #A1\u5e94\u7528\u4e8e\u6587\u4ef6\u56de\u6eda log4j.appender.A1=org.apache.log4j.RollingFileAppender log4j.appender.A1.File=${webapp.root}/WEB-INF/logs/org.log log4j.appender.A1.MaxFileSize=500KB log4j.appender.A1.MaxBackupIndex=50 log4j.appender.A1.Append=true log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - [%p] [%C{1}] - %m%n #A2\u5e94\u7528\u4e8e\u6587\u4ef6\u56de\u6eda log4j.appender.A2=org.apache.log4j.RollingFileAppender log4j.appender.A2.File=${webapp.root}/WEB-INF/logs/com.log log4j.appender.A2.MaxFileSize=500KB log4j.appender.A2.MaxBackupIndex=50 log4j.appender.A2.Append=true log4j.appender.A2.layout=org.apache.log4j.PatternLayout log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - [%p] [%C{1}] - %m%n ######################## # JDBC Appender ####################### #log4j.logger.business=INFO,db #log4j.appender.db=com.neam.commons.MyJDBCAppender log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender log4j.appender.db.BufferSize=1 log4j.appender.db.driver=net.sourceforge.jtds.jdbc.Driver log4j.appender.db.URL=jdbc:jtds:sqlserver://localhost:9433;DatabaseName=infor_manage #enter log4j.appender.db.user=sa log4j.appender.db.password=123@sports log4j.appender.db.sql=insert into operate_log(class,method,createtime,loglevel,logmsg,user_id,user_type) values ('%C','%M','%d{yyyy-MM-dd HH\:mm\:ss}','%p','%m','1',1) log4j.appender.db.layout=org.apache.log4j.PatternLayout
這次的配置要復雜點
log4j.logger.xidian=INFO,db
log4j.logger.org=WARN, A1
log4j.logger.com =WARN, A2
這個配置就是將不同的包下的信息輸出到不同的文件中,根據下面的配置可知以xidian開頭的包下的java文件的日志信息時進行數據庫存儲的,而org與com包開頭的日志信息是輸出到文件中,文件的輸出地址是${webapp.root}/WEB-INF/logs/org.log即項目的WEB-INF目錄下的logs文件夾中,為了得到${webapp.root}我們還需要到web.xml文件中進行配置:
<!--由Sprng載入的Log4j配置文件位置--> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.properties</param-value> </context-param> <!--Spring log4j Config listener--> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
這樣我們再次運行HelloWorld程序
查看數據庫(很干凈了,哈哈):
然后在到WEB-INF目錄下的logs文件夾中查看輸出的日志文件:
由於我們在根Logger下也進行了配置:這個根Logger的配置是對所有日志操作都是有作用的
log4j.rootLogger=INFO,stdout #stdout\u5e94\u7528\u4e8e\u63a7\u5236\u53f0 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%d %5p (%c\:%L) - %m%n
這個配置是進行控制太的輸出,因此我們在控制台中也會發現有輸出:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
到此為止我們日志的基本操作都應該掌握了,但我還有一個需求沒有滿足,就是我一開始數據庫字段的設計中還包含了兩個字段:
這兩個字段是存儲用戶的id和用戶類型的,以便我們在后面日志的核查中能正確的找出用戶信息;但這里就有一個問題了我們就靠上面的操作還是不能將用戶信息得到的,
還有log4j的設計者已經為我們想到了,log4j為我們提供了MDC(MDC是log4j種非常有用類,它們用於存儲應用程序的上下文信息(context infomation),從而便於在log中使用這些上下文信息。MDC內部使用了類似map的機制來存儲信息,上下文信息也是每個線程獨立地儲存,所不同的是信息都是以它們的key值存儲在”map”中。相對應的方法,
MDC.put(key, value); MDC.remove(key); MDC.get(key); 在配置PatternLayout的時候使用:%x{key}來輸出對應的value。
思路:我們就可以利用過濾器來得到登錄用戶的信息,然后將其存儲到MDC中,然后再在log4j.properties配置文件中的sql語句中進行讀取:
過濾器代碼:
package xidian.sl.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.log4j.MDC; public class LogResFilter implements Filter { private final static double DEFAULT_USERID= 0.0; @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //System.out.println("進入過濾器"); HttpServletRequest req=(HttpServletRequest)request; HttpSession session= req.getSession(); if (session==null){ MDC.put("userId",DEFAULT_USERID); MDC.put("userType",DEFAULT_USERID); } else{ //StuInfor stuInfor =(StuInfor)session.getAttribute("admin"); //用戶的id Integer userId = (Integer)session.getAttribute("userId"); //用戶的類型 String adminType = (String)session.getAttribute("adminType"); if (userId == null&& adminType == null){ MDC.put("userId",DEFAULT_USERID); MDC.put("userType",DEFAULT_USERID); } else { System.out.println("用戶id"+userId+ "類型"+ adminType); MDC.put("userId", userId); MDC.put("userType", adminType); } } chain.doFilter(request, response); } @Override public void init(FilterConfig arg0) throws ServletException { } }
然后在web.xml中進行過濾器的配置:
<filter> <filter-name>LogResFilter</filter-name> <filter-class>xidian.sl.filter.LogResFilter</filter-class> </filter> <filter-mapping> <filter-name>LogResFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
這樣用戶登錄后存儲在session中的信息通過該過濾器就存儲MDC中,然后我們在日志文件中寫sql語句:
log4j.appender.db.sql=insert into operate_log(class,method,createtime,loglevel,logmsg,user_id,user_type) values ('%C','%M','%d{yyyy-MM-dd HH\:mm\:ss}','%p','%m','%X{userId}','%X{adminType}')
到這里我的需求基本上滿足了,不知道有沒有滿足你的需求。