29. privateString formatCols(Map
32. for(Map.Entry
36. sb.append(entry.getKey()).append(\).append(entry.getValue()); 37. first =false; 38. }
39. return sb.toString(); 40. } 41. }
在上面的例子中,可以通过添加 MarkerFilters 来打印query的日志, update的日志,或者所有的日志。
在使用Markers有一些重要规则
(1) Markers必须是唯一的。他们是不可变的注册名称,要确保你的应用中的Markers都是不同的。
(2) 父 Markers可以被动态的添加或者移除,但是这个操作是很昂贵的,建议在第一次获得Markers时就确定父
Markers。
(3) 恰当的设置父类 Markers。
3. EventLogger
EventLogger类提供了一个在应用中产生 logging events的简单机制。EventLogger是初始化 日志审计系统中事件的一个很有用的方式。
一个推荐的使用方法是在 WEBapplication环境中, 可以使用request 中的内容填充ThreadContext Map,例如用户ID,用户的IP地址,产品名称等。这可以在一个servlet filter 中很简单的实现,并且在request 的结尾,ThreadContext Map 将被清除。
1. import org.apache.logging.log4j.ThreadContext; 2. import org.apache.commons.lang.time.DateUtils; 3.
4. import javax.servlet.Filter; 5. import javax.servlet.FilterConfig; 6. import javax.servlet.ServletException; 7. import javax.servlet.ServletRequest; 8. import javax.servlet.ServletResponse; 9. import javax.servlet.FilterChain; 10. import javax.servlet.http.HttpSession; 11. import javax.servlet.http.HttpServletRequest; 12. import javax.servlet.http.Cookie;
13. import javax.servlet.http.HttpServletResponse; 14. import java.io.IOException; 15. import java.util.TimeZone; 16.
17. publicclassRequestFilterimplementsFilter{ 18. privateFilterConfig filterConfig;
19. privatestaticString TZ_NAME =\; 20.
21. publicvoid init(FilterConfig filterConfig)throwsServletException{ 22. this.filterConfig = filterConfig; 23. } 24. 25. /**
26. * Sample filter that populates the MDC on every request. 27. */
28. publicvoid doFilter(ServletRequest servletRequest,ServletResponse
servletResponse,FilterChain filterChain) 29. throwsIOException,ServletException{
30. HttpServletRequest request =(HttpServletRequest)servletRequest; 31. HttpServletResponse response =(HttpServletResponse)servletResponse; 32. ThreadContext.put(\, request.getRemoteAddr()); 33. HttpSession session = request.getSession(false); 34. TimeZone timeZone =null; 35. if(session !=null){
36. // Something should set this after authentication completes 37. String loginId =(String)session.getAttribute(\); 38. if(loginId !=null){
39. ThreadContext.put(\, loginId); 40. }
41. // This assumes there is some javascript on the user's page to create the cookie. 42. if(session.getAttribute(TZ_NAME)==null){ 43. if(request.getCookies()!=null){
44. for(Cookie cookie : request.getCookies()){ 45. if(TZ_NAME.equals(cookie.getName())){
46. int tzOffsetMinutes =Integer.parseInt(cookie.getValue()); 47. timeZone =TimeZone.getTimeZone(\);
48. timeZone.setRawOffset((int)(tzOffsetMinutes *DateUtils.MILLIS_PER_MINUTE)); 49. request.getSession().setAttribute(TZ_NAME, tzOffsetMinutes); 50. cookie.setMaxAge(0); 51. response.addCookie(cookie); 52. } 53. } 54. } 55. } 56. }
57. ThreadContext.put(\, servletRequest.getServerName());
58. ThreadContext.put(\, filterConfig.getInitParameter(\)); 59. Threadcontext.put(\, servletRequest.getLocale().getDisplayName()); 60. if(timeZone ==null){
61. timeZone =TimeZone.getDefault(); 62. }
63. ThreadContext.put(\, timeZone.getDisplayName()); 64. filterChain.doFilter(servletRequest, servletResponse); 65. ThreadContext.clear(); 66. } 67.
68. publicvoid destroy(){ 69. } 70. }
当需要通过StructuredDataMessage 记录时间时,可以创建和为StructuredDataMessage 赋值,然后调用 EventLogger.logEvent(msg)
1. import org.apache.logging.log4j.StructuredDataMessage; 2. import org.apache.logging.log4j.EventLogger; 3.
4. import java.util.Date; 5. import java.util.UUID; 6.
7. publicclassMyApp{ 8.
9. publicString doFundsTransfer(Account toAccount,Account fromAccount,long amount){ 10. toAccount.deposit(amount); 11. fromAccount.withdraw(amount);
12. String confirm = UUID.randomUUID().toString();
13. StructuredDataMessage msg =newStructuredDataMessage(confirm,null,\); 14. msg.put(\, toAccount); 15. msg.put(\, fromAccount); 16. msg.put(\, amount); 17. EventLogger.logEvent(msg); 18. return confirm; 19. } 20. }
EventLogger 类使用一个名字为EventLogger 的Logger。EventLogger使用一个OFF的默认日志级别,它不可以被过滤,这个事件可以通过StructuredDataLayout.来设置格式。
4. Messages
一般情况下, Logger接收传入的参数打印日志。比如一个应用中有一个 Map,其值如下 : {\\,还有一个User对象,其中有getId 方法返回 \值。开发者可能打印这些信息如下 :
logger.info(\, map.get(\), user.getId()); 会打印出 \的日志。 另一种方式是使用一个 Message来打印这个日志 :
logger.info(newLoggedInMessage(map, user)); 这种方式获取日志内容委托给了LoggedInMessage 的getFormattedMessage 方法。虽然这种方式创建了一个新的对象,但是在LoggedInMessage格式化以前,并不会调用 map和user的任何方法。这在你的Object对象的toString方法不能产生你想要的日志信息时很有用。
另一个好处是 Message简化了输出日志的Layout。 在其他日志框架中,Layout必须通过各自的参数来循环,并根据遇到的对象来确定做什么。但是 Messages的 Layout可以选择使用Message的格式化代理 或者基于遇到的Message类型来格式化。
以前面的打印 SQL的例子来说,如果要使用Message的方式打印日志。 首先定义Message 。
1. publicclassSQLMessageimplementsMessage{ 2. publicenumSQLType{ 3. UPDATE, 4. QUERY 5. }; 6.
7. privatefinalSQLType type; 8. privatefinalString table;
9. privatefinalMap
11. publicSQLMessage(SQLType type,String table){ 12. this(type, table,null); 13. } 14.
15. publicSQLMessage(SQLType type,String table,Map
21. publicString getFormattedMessage(){ 22. switch(type){ 23. case UPDATE:
24. return createUpdateString(); 25. break; 26. case QUERY:
27. return createQueryString(); 28. break; 29. default; 30. } 31. } 32.
33. publicString getMessageFormat(){ 34. return type +\+ table; 35. } 36.
37. publicObject getParameters(){ 38. return cols; 39. } 40.