@@ -34,8 +34,7 @@ <h1>SLF4J extensions</h1>
3434 < ul >
3535 < li > < a href ="#profiler "> Profiler</ a > </ li >
3636 < li > < a href ="#extended_logger "> Extended logger</ a > </ li >
37- < li > < a href ="#event_logger "> Event Logging</ a > </ li >
38- < li > < a href ="#javaagent "> Logging added with Java agent (requires Java 5)</ a > </ li >
37+
3938 </ ul >
4039
4140 < h2 > < a name ="profiler "> </ a > Profilers</ h2 >
@@ -590,301 +589,6 @@ <h2><a name="extended_logger"></a>Extended Logger</h2>
590589
591590 <!-- .............................................................. -->
592591
593- < h2 > < a name ="event_logger "> </ a > Event Logging</ h2 >
594-
595- < p > The EventLogger class provides a simple mechanism for logging events that occur in an application.
596- While the EventLogger is useful as a way of initiating events that should be processed by an audit
597- Logging system, it does not implement any of the features an audit logging system would require
598- such as guaranteed delivery.</ p >
599-
600- < p > The recommended way of using the EventLogger in a typical web application is to populate
601- the SLF4J MDC with data that is related to the entire lifespan of the request such as the user's id,
602- the user's ip address, the product name, etc. This can easily be done in a servlet filter where
603- the MDC can also be cleared at the end of the request. When an event that needs to be recorded
604- occurs an EventData object should be created and populated. Then call EventLogger.logEvent(data)
605- where data is a reference to the EventData object.</ p >
606-
607- < pre class ="prettyprint source "> import org.slf4j.MDC;
608- import org.apache.commons.lang.time.DateUtils;
609-
610- import javax.servlet.Filter;
611- import javax.servlet.FilterConfig;
612- import javax.servlet.ServletException;
613- import javax.servlet.ServletRequest;
614- import javax.servlet.ServletResponse;
615- import javax.servlet.FilterChain;
616- import javax.servlet.http.HttpSession;
617- import javax.servlet.http.HttpServletRequest;
618- import javax.servlet.http.Cookie;
619- import javax.servlet.http.HttpServletResponse;
620- import java.io.IOException;
621- import java.util.TimeZone;
622-
623- public class RequestFilter implements Filter
624- {
625- private FilterConfig filterConfig;
626- private static String TZ_NAME = "timezoneOffset";
627-
628- public void init(FilterConfig filterConfig) throws ServletException {
629- this.filterConfig = filterConfig;
630- }
631-
632- /**
633- * Sample filter that populates the MDC on every request.
634- */
635- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
636- FilterChain filterChain) throws IOException, ServletException {
637- HttpServletRequest request = (HttpServletRequest)servletRequest;
638- HttpServletResponse response = (HttpServletResponse)servletResponse;
639- MDC.put("ipAddress", request.getRemoteAddr());
640- HttpSession session = request.getSession(false);
641- TimeZone timeZone = null;
642- if (session != null) {
643- // Something should set this after authentication completes
644- String loginId = (String)session.getAttribute("LoginId");
645- if (loginId != null) {
646- MDC.put("loginId", loginId);
647- }
648- // This assumes there is some javascript on the user's page to create the cookie.
649- if (session.getAttribute(TZ_NAME) == null) {
650- if (request.getCookies() != null) {
651- for (Cookie cookie : request.getCookies()) {
652- if (TZ_NAME.equals(cookie.getName())) {
653- int tzOffsetMinutes = Integer.parseInt(cookie.getValue());
654- timeZone = TimeZone.getTimeZone("GMT");
655- timeZone.setRawOffset((int)(tzOffsetMinutes * DateUtils.MILLIS_PER_MINUTE));
656- request.getSession().setAttribute(TZ_NAME, tzOffsetMinutes);
657- cookie.setMaxAge(0);
658- response.addCookie(cookie);
659- }
660- }
661- }
662- }
663- }
664- MDC.put("hostname", servletRequest.getServerName());
665- MDC.put("productName", filterConfig.getInitParameter("ProductName"));
666- MDC.put("locale", servletRequest.getLocale().getDisplayName());
667- if (timeZone == null) {
668- timeZone = TimeZone.getDefault();
669- }
670- MDC.put("timezone", timeZone.getDisplayName());
671- filterChain.doFilter(servletRequest, servletResponse);
672- MDC.clear();
673- }
674-
675- public void destroy() {
676- }
677- } </ pre >
678-
679- < p > Sample class that uses EventLogger.</ p >
680- < pre class ="prettyprint source "> import org.slf4j.ext.EventData;
681- import org.slf4j.ext.EventLogger;
682-
683- import java.util.Date;
684- import java.util.UUID;
685-
686- public class MyApp {
687-
688- public String doFundsTransfer(Account toAccount, Account fromAccount, long amount) {
689- toAccount.deposit(amount);
690- fromAccount.withdraw(amount);
691- EventData data = new EventData();
692- data.setEventDateTime(new Date());
693- data.setEventType("transfer");
694- String confirm = UUID.randomUUID().toString();
695- data.setEventId(confirm);
696- data.put("toAccount", toAccount);
697- data.put("fromAccount", fromAccount);
698- data.put("amount", amount);
699- EventLogger.logEvent(data);
700- return confirm;
701- }
702- } </ pre >
703-
704- < p > The EventLogger class uses a Logger named "EventLogger". EventLogger uses a logging level
705- of INFO. The following shows a configuration using Logback.</ p >
706- < pre class ="prettyprint source "> <configuration>
707- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
708- <layout class="ch.qos.logback.classic.PatternLayout">
709- <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
710- </layout>
711- </appender>
712-
713- <appender name="events" class="ch.qos.logback.core.ConsoleAppender">
714- <layout class="ch.qos.logback.classic.PatternLayout">
715- <Pattern>%d{HH:mm:ss.SSS} %X - %msg%n</Pattern>
716- </layout>
717- </appender>
718-
719- <logger name="EventLogger" additivity="false">
720- <level value="INFO"/>
721- <appender appender-ref="events"/>
722- </logger>
723-
724- <root level="DEBUG">
725- <appender-ref ref="STDOUT" />
726- </root>
727-
728- </configuration> </ pre >
729-
730- <!-- .............................................................. -->
731-
732- < h2 > < a name ="javaagent "> </ a > Adding logging with Java agent</ h2 >
733-
734- < p > < b > NOTE: BETA RELEASE, NOT PRODUCTION QUALITY</ b > </ p >
735-
736- < p > Quickstart for the impatient:</ p >
737-
738- < ol >
739- < li > Use Java 5 or later.</ li >
740- < li > Download slf4j-ext-${latest.stable.version}.jar and javassist.jar, and put them
741- both in the same directory.</ li >
742- < li > Ensure your application is properly configured with
743- slf4j-api-${latest.stable.version}.jar and a suitable backend.</ li >
744-
745- < li > Instead of "java ..." use "java --javaagent:PATH/slf4j-ext-${latest.stable.version}.jar=time,verbose,level=info ..."
746- < br />
747- (replace PATH with the path to the jar)
748- </ li >
749- < li > That's it!</ li >
750- </ ol >
751-
752- < p > In some applications logging is used to trace the actual
753- execution of the application as opposed to log an occasional event.
754- One approach is using the < a href ="#extended_logger "> extended
755- logger</ a > to add statements as appropriately, but another is to use
756- a tool which modifies compiled bytecode to add these statements!
757- Many exist, and the one included in slf4j-ext is not intended to
758- compete with these, but merely provide a quick way to get very basic
759- trace information from a given application.
760- </ p >
761-
762- < p > Java 5 added the Java Instrumentation mechanism, which allows you
763- to provide "Java agents" that can inspect and modify the byte code
764- of the classes as they are loaded. This allows the original class
765- files to remain unchanged, and the transformations done on the byte
766- codes depend on the needs at launch time.
767- </ p >
768-
769- < p > Given the well-known "Hello World" example:</ p >
770-
771- < pre class ="prettyprint source "> public class HelloWorld {
772- public static void main(String args[]) {
773- System.out.println("Hello World");
774- }
775- }</ pre >
776-
777- < p > a typical transformation would be similar to: (imports omitted)</ p >
778-
779- < pre class ="prettyprint source "> public class LoggingHelloWorld {
780- final static Logger _log = LoggerFactory.getLogger(LoggingHelloWorld.class.getName());
781-
782- public static void main(String args[]) {
783- if (_log.isInfoEnabled()) {
784- _log.info("> main(args=" + Arrays.asList(args) + ")");
785- }
786- System.out.println("Hello World");
787- if (_log.isInfoEnabled()) {
788- _log.info("< main()");
789- }
790- }
791- }</ pre >
792-
793- < p > which in turn produces the following result when run similar to
794- "java LoggingHelloWorld 1 2 3 4":
795- </ p >
796-
797- < p class ="source "> 1 [main] INFO LoggingHelloWorld - > main(args=[1, 2, 3, 4])
798- Hello World
799- 1 [main] INFO LoggingHelloWorld - < main()</ p >
800-
801- < p > The same effect could have been had by using this command (with
802- the relative path to javassist.jar and
803- slf4j-ext-${latest.stable.version}.jar being ../jars):</ p >
804-
805- < p class ="source "> java -javaagent:../jars/slf4j-ext-${latest.stable.version}.jar HelloWorld 1 2 3 4</ p >
806-
807- < p > </ p >
808-
809-
810- < h3 > How to use</ h3 >
811- < p > The javaagent may take one or more options separated by comma. The following options
812- are currently supported:</ p >
813-
814- < dl >
815- < dt > < b > level</ b > =X</ dt >
816- < dd > The log level to use for the generated log statements. X is
817- one of "info", "debug" or "trace". Default is "info".</ dd >
818-
819- < dt > < b > time</ b > </ dt >
820- < dd > Print out the current date at program start, and again when
821- the program ends plus the execution time in milliseconds.</ dd >
822-
823- < dt > < b > verbose</ b > </ dt >
824- < dd > Print out when a class is processed as part of being loaded</ dd >
825-
826- < dt > < b > ignore</ b > =X:Y:...</ dt >
827- < dd > (Advanced) Provide full list of colon separated prefixes of
828- class names NOT to add logging to. The default list is
829- "org/slf4j/:ch/qos/logback/:org/apache/log4j/". This does not override the fact that a class must be able to access the
830- slf4j-api classes in order to do logging, so if these classes are not visible to a given class it is not instrumented.
831- </ dd >
832- </ dl >
833-
834-
835- < p > Some classes may misbehave when being rendered with "object.toString()" so they may be explicitly disabled
836- in the logback configuration file permanently. For instance the ToStringBuilder in the Apache Jakarta commons lang
837- package is a prime candidate for this. For logback add this snippet to logback.xml:
838- < pre > <logger name="org.apache.commons.lang.builder" level="OFF" /></ pre >
839- </ p >
840-
841-
842-
843- < p > Note: These are not finalized yet, and may change.</ p >
844-
845- < h3 > Locations of jar files</ h3 >
846-
847- < p > The javassist library is used for the actual byte code
848- manipulation and must be available to be able to add any logging
849- statements. slf4j-ext-${latest.stable.version} has been configured to
850- look for the following:
851- </ p >
852-
853- < ul >
854- < li > "javassist-3.4.GA.jar" relatively to
855- slf4j-ext-${latest.stable.version}.jar as would be if Maven had downloaded
856- both from the repository and slf4j-ext-${latest.stable.version}.jar was
857- referenced directly in the Maven repository in the
858- "-javaagent"-argument.</ li >
859- < li > "javassist-3.4.GA.jar" in the same directory as slf4j-ext</ li >
860- < li > "javassist.jar" in the same directory as slf4j-ext</ li >
861- </ ul >
862-
863- < p > A warning message is printed if the javassist library was not
864- found by the agent, and options requiring byte code transformations will not work.
865- </ p >
866-
867-
868- < h3 > Misc notes</ h3 >
869-
870- < ul >
871- < li > A java agent is not invoked on any classes already loaded by the
872- class loader.</ li >
873- < li > Exceptions in the java agent that would normally have been
874- printed, may be silently swallowed by the JVM.</ li >
875- < li > The javaagent only logs to System.err.</ li >
876- < li > The name of the logger variable is fixed (to a value unlikely to be used) so if that
877- name is already used, a failure occurs. This should be changed to determine
878- an unused name and use that instead.</ li >
879- < li > Empty methods are not instrumented (an incorrect check for an interface). They should be</ li >
880-
881- </ ul >
882-
883- < p > (The agent is an adaption of the java.util.logging version
884- described in < a
885- href ="http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html "
886- > http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html</ a > )
887- </ p >
888592
889593 < script src ="templates/footer.js " type ="text/javascript "> </ script >
890594 </ div >
0 commit comments