EventLogger 类提供了一个简单的机制,这个机制可用于记录应用中发生的事件。
EventLogger 的推荐使用方式,例如在网站应中,将数据填入到 SLF4J 的 MDC,这里数据贯 穿一个请求的始末,这些数据中包含用户 ID,用户的 IP 地址,商品名称等等。这些可以非常容易 地在 Servlet 过滤器(filter)中完成,在这里 MDC 也可以请求结束后清理掉。当一个事件需 要被记录并重现时,一个 EventData 应该被创建并发布。然后调用 EventLogger.logEvent(data) 这里 data 就是指向 EventData 对象的引用。
import org.slf4j.MDC; import org.apache.commons.lang.time.DateUtils; import javax.servlet.Filter; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.FilterChain; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.TimeZone; public class RequestFilter implements Filter { private FilterConfig filterConfig; private static String TZ_NAME = "timezoneOffset"; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } /** * Sample filter that populates the MDC on every request. */ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)servletRequest; HttpServletResponse response = (HttpServletResponse)servletResponse; MDC.put("ipAddress", request.getRemoteAddr()); HttpSession session = request.getSession(false); TimeZone timeZone = null; if (session != null) { // Something should set this after authentication completes String loginId = (String)session.getAttribute("LoginId"); if (loginId != null) { MDC.put("loginId", loginId); } // This assumes there is some javascript on the user's page to create the cookie. if (session.getAttribute(TZ_NAME) == null) { if (request.getCookies() != null) { for (Cookie cookie : request.getCookies()) { if (TZ_NAME.equals(cookie.getName())) { int tzOffsetMinutes = Integer.parseInt(cookie.getValue()); timeZone = TimeZone.getTimeZone("GMT"); timeZone.setRawOffset((int)(tzOffsetMinutes * DateUtils.MILLIS_PER_MINUTE)); request.getSession().setAttribute(TZ_NAME, tzOffsetMinutes); cookie.setMaxAge(0); response.addCookie(cookie); } } } } } MDC.put("hostname", servletRequest.getServerName()); MDC.put("productName", filterConfig.getInitParameter("ProductName")); MDC.put("locale", servletRequest.getLocale().getDisplayName()); if (timeZone == null) { timeZone = TimeZone.getDefault(); } MDC.put("timezone", timeZone.getDisplayName()); filterChain.doFilter(servletRequest, servletResponse); MDC.clear(); } public void destroy() { } }使用 EventLogger 的示例类。
import org.slf4j.ext.EventData; import org.slf4j.ext.EventLogger; import java.util.Date; import java.util.UUID; public class MyApp { public String doFundsTransfer(Account toAccount, Account fromAccount, long amount) { toAccount.deposit(amount); fromAccount.withdraw(amount); EventData data = new EventData(); data.setEventDateTime(new Date()); data.setEventType("transfer"); String confirm = UUID.randomUUID().toString(); data.setEventId(confirm); data.put("toAccount", toAccount); data.put("fromAccount", fromAccount); data.put("amount", amount); EventLogger.logEvent(data); return confirm; } }EventLogger 类使用一个被命名为 EventLogger 的日志。EventLogger 使用 INFO 级别 的日志。下面是一个使用 Logback 的配置。
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> </layout> </appender> <appender name="events" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{HH:mm:ss.SSS} %X - %msg%n</Pattern> </layout> </appender> <logger name="EventLogger" additivity="false"> <level value="INFO"/> <appender appender-ref="events"/> </logger> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>入门概要:
Use Java 5 or later.使用 Java 5 或者更高。Download slf4j-ext-1.7.19.jar and javassist.jar, and put them both in the same directory.下载 slf4j-ext-1.7.19.jar 和 javassist.jar,并将它们放置在同一个目录中。Ensure your application is properly configured with slf4j-api-1.7.19.jar and a suitable backend.确保你的应用已经恰当地配置 slf4j-api-1.7.19.jar 和一个适当的后端。Instead of “java …” use “java –javaagent:PATH/slf4j-ext-1.7.19.jar=time,verbose,level=info …” (replace PATH with the path to the jar)使用 java --javaagent:PATH/slf4j-ext-1.7.19.jar=time,verbose,level=info … (使用指向 Jar 的路径来替换 PATH) 代替 java … That’s it!就这些!在一些应用中,日志是用于追踪程序实际执行情况的,而不是用于记录偶然发生的事情。一种实现方 式是使用扩展日志在程序适当的地方添加语句;但还有一种方式,是使用工具通过修改编译后的字节 码的方式来添加语句。还有其他很多方式存在,当时包含在 slf4j-ext 中的并不是为比较而生的。 这只是提供了一个方法,在既定程序中,快速获取基本的追踪信息。
在 Java 5 中,增加了 Instrumentation 机制,这个机制提供了 Java 代理(agent)功能,它 允许你在字节码加载时,检查和修改字节码。这就可以让原来的类文件保存不变,这种字节码转换只 是在需要加载时才进行。
public class HelloWorld { public static void main(String args[]) { System.out.println("Hello World"); } }一个典型的转换入下: (import 语句别忽略)
public class LoggingHelloWorld { final static Logger _log = LoggerFactory.getLogger(LoggingHelloWorld.class.getName()); public static void main(String args[]) { if (_log.isInfoEnabled()) { _log.info("> main(args=" + Arrays.asList(args) + ")"); } System.out.println("Hello World"); if (_log.isInfoEnabled()) { _log.info("< main()"); } } }当执行类似 java LoggingHelloWorld 1 2 3 4 时,输出也大致如下:
1 [main] INFO LoggingHelloWorld - > main(args=[1, 2, 3, 4]) Hello World 1 [main] INFO LoggingHelloWorld - < main()可以使用下面的命令,来达到同样的效果(javassist.jar 和 slf4j-ext-1.7.19.jar 放在了相对路径 ../jars 中)
java -javaagent:../jars/slf4j-ext-1.7.19.jar HelloWorld 1 2 3 4javaagent 可以指定一到多个使用逗号分割的选项。所支持的选项如下:
level=X The log level to use for the generated log statements. X is one of “info”, “debug” or “trace”. Default is “info”.对于生成日志语句所使用的日志级别。其中 X 可取的值为: info、 debug、 trace。默认的级别为: info。 time 打印出程序启动的当前日期,并且在程序结尾再输出以毫秒计算的程序执行时间。 verbose Print out when a class is processed as part of being loaded打印出当一个类被装载部分的处理 ignore=X:Y:… (高级特性)提供不不需要输出日志的类名前缀,使用冒号分割。默认的列表为: org/slf4j/:ch/qos/logback/:org/apache/log4j/。 这还有一个不言自明的事实就是,为了能够输出日志,类必须能够访问 slf4j-api 的类,如果这 些类不能访问给点的类,则肯定不能重塑。一些类使用 object.toString() 进行呈现时,也许变现行为不当。所以,应该在 logback 配 置文件明确声明日志不可用。在 Apache Jakarta commons lang 包中的 ToStringBuilder 就 是一个极好的例子。对于 logback,可以将下面这个代码片段添加到 logback.xml 中:
<logger name="org.apache.commons.lang.builder" level="OFF" />这些还没有最终确定,也许还可能会变。
javassist 库是用来实际进行字节码操纵的,为了添加任意的日志语句,它必须可用。slf4j-ext-1.7.19 可以像下面这样进行配置:
“javassist-3.4.GA.jar” 和 “slf4j-ext-1.7.19.jar” 可以使用 Maven 从仓库下载。而且, “slf4j-ext-1.7.19.jar” 在 Maven 库中,直接以 “-javaagent” 参数引用。“javassist-3.4.GA.jar” 和 “slf4j-ext” 在同一个目录下。当 javassist 没有被代理发现的话,会打印出警告信息。并且指定的字节码转换也不会工作。
(Java 代理是 java.util.logging 版本的一种适配。具体描述在 http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html。)
转载自 并发编程网 - ifeve.com
相关资源:slf4j-1.6.4jar包(完整版)