由於最近正在擴充衛生局考務系統,由於上一次使用過一次該系統後,發現很多考生最後出現說已列印報名表但卻在資料庫中找不到她的報名記錄;
因此為了以後有依據,將所有使用者的動作記錄檔案寫入資料供管理員查詢成為了這次擴張項目的一個內容;
這裡我決定使用的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,stdoutlog4j.logger.xidian=INFO,db log4j.logger.org=WARN, A1log4j.logger.com =WARN, A2#stdout\u5e94\u7528\u4e8e\u63a7\u5236\u53f0log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.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\u6edalog4j.appender.A1=org.apache.log4j.RollingFileAppenderlog4j.appender.A1.File=${webapp.root}/WEB-INF/logs/org.loglog4j.appender.A1.MaxFileSize=500KBlog4j.appender.A1.MaxBackupIndex=50log4j.appender.A1.Append=truelog4j.appender.A1.layout=org.apache.log4j.PatternLayoutlog4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} - [%p] [%C{1}] - %m%n#A2\u5e94\u7528\u4e8e\u6587\u4ef6\u56de\u6edalog4j.appender.A2=org.apache.log4j.RollingFileAppenderlog4j.appender.A2.File=${webapp.root}/WEB-INF/logs/com.loglog4j.appender.A2.MaxFileSize=500KBlog4j.appender.A2.MaxBackupIndex=50log4j.appender.A2.Append=truelog4j.appender.A2.layout=org.apache.log4j.PatternLayoutlog4j.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\u53f0log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.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}')
到這裡我的需求基本上滿足了,不知道有沒有滿足你的需求。