The Art of Logging: An Exploration with Apache Log4j 2 by Gary Gregory

Slide Note
Embed
Share

Delve into the world of logging with Apache Log4j 2 through the insightful exploration presented by Gary Gregory, a Principal Software Engineer at Rocket Software. Discover the importance of logging, key concepts like logging architecture and APIs, and the significance of modern logging frameworks such as Apache Log4j 2 and SLF4J with Logback.


Uploaded on Sep 28, 2024 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

E N D

Presentation Transcript


  1. The Art of Logging An exploration with Apache Log4j 2 Gary Gregory garydgregory@gmail.com

  2. Gary Gregory Principal Software Engineer, Rocket Software C Smalltalk Java Apache: Commons, Log4j, HttpComponents, Xalan Books

  3. Thank you Dealer.com for sponsoring this meeting and the Pumpkin Race!

  4. Why log? Logs are like car insurance. Nobody wants to pay for it, but when something goes wrong everyone wants the best available Pablo Kraan

  5. The Road to Nowhere (0) System.out.println("Starting " + new Date()); System.console().printf("Starting %s , new Date());

  6. The Road to Nowhere (1) public void test() { println("Starting " + new Date()); } private void println(String message) { System.out.println(message); }

  7. The Road to Nowhere (2) public static boolean DEBUG = true; public void test() { println("Starting " + new Date()); } private void println(String message) { if (DEBUG) { System.out.println(message); } }

  8. The Road to Nowhere (3) public static boolean DEBUG = Boolean.getBoolean( EBUG"); public void test() { println("Starting " + new Date()); } private void println(String message) { if (DEBUG) { System.out.println(message); } }

  9. The Road to Nowhere (4) Destinations Console out, err. Files, what format(s)? And so on Event levels Source location? Timestamp formats Thread info? Configuration On and on and on

  10. Key Concepts: architecture Split Logging API and implementations Pluggable implementations Modern logging frameworks do this Apache Log4j 2 SLF4J + Logback Apache Commons Logging (API only) JUL Log4j 1 did not, all-in-one

  11. Log4j API architecture

  12. Pluggable implementation: SLF4J

  13. Key concepts API Configuration Customization/Pluggability

  14. Key concepts APIs LogManager Level Logger Marker More: Flow tracing with fish tagging Context stack and context map

  15. LogManager Creates Loggers LogManager.getLogger( com.example.SomeClass ); LogManager.getLogger(SomeClass.class); LogManager.getLogger();

  16. LogManager and Message Factories Message factories Login OK for user {} from terminal {} Login OK for user {0} from terminal {1} Login OK for user %s from terminal %s ETL processed %,d records

  17. Message Factories org.apache.logging.log4j.LogManager.getLogger(Class|String|Object, MessageFactory) FormattedMessageFactory LocalizedMessageFactory MessageFormatMessageFactory ParameterizedMessageFactory ParameterizedNoReferenceMessageFactory SimpleMessageFactory StringFormatterMessageFactory

  18. Levels How important is this event? Who needs to know about this event? Custom levels

  19. 6 built-in Levels

  20. Using Levels in applications INFO : User Alice logged in. WARN : User Bob entered an invalid password. ERROR: User Bob entered an invalid password three times, user locked out. DEBUG: Loading user Bob from database jdbc:... INFO : Emailed user Bob: three login attempts rejected, user locked out. ERROR: Email to Alice bounced back; subject: Your weekly summary.

  21. Understanding Levels vs. Loggers Message: What it is I am telling my audience? Levels How important is this? Loggers Who s talking now?

  22. Hierachical Loggers Loggers are named Names are hierarchical: a.b.c, a,b, a,

  23. Hierachical loggers example Kitchen Kitchen.Refrigerator Kitchen.Oven Kitchen.Refrigerator.Door Kitchen.Oven.BottomOven Kitchen.Refrigerator.Filter Kitchen.Oven.BottomOven.Door Kitchen.Refrigerator.Light Kitchen.Oven.BottomOven.Timer Kitchen.Refrigerator.Freezer.Door Kitchen.Oven.BottomOven.Light Kitchen.Refrigerator.Freezer.Light Kitchen.Oven.TopOven.Door And so on Kitchen.Oven.TopOven.Timer Kitchen.Oven.TopOven.Light Kitchen.Dishwasher

  24. Logger configuration Hierarchical names: com.example.app.foo Configure a logger or whole hierarchy: <Root level= warn"> <Logger name="javax.management" level="INFO" /> <Logger name= com.example.app" level="INFO" /> <Logger name= com.example.app.foo" level= DEBUG" /> <Logger name= com.example.app.bar" level= TRACE" />

  25. Using a Logger logger.debug( User {} logged in , user); logger.info( User {} logged in , user);

  26. Logger granularity One global logger The root logger Nah. One per class Use the FQCN One per instance? In specific cases Use the FQCN + instance info Watch for lots of short lived objects Examples

  27. Clever logger names One per instance Must use the logger name to uniquely identify a logger com.example.server.socket.8080 com.example.rest.get.GetCustomer

  28. Getting to advanced logging (0) logger.debug("There are " + getUserCount() + " users logged in now."); You are always building the full string before calling debug() You are always calling getUserCount() Both of the above happens even if debug logging is disabled!

  29. Getting to advanced logging (1) Guard it: if (logger.isDebugEnabled()) { logger.debug("There are " + getUserCount() + " users logged in now."); }

  30. Getting to advanced logging (3) Parameterize it: logger.debug("There are {} users logged in now.", getUserCount());

  31. Getting to advanced logging (4) Use lambdas! logger.debug("There are {} users logged in now.", () -> getUserCount());

  32. Getting to advanced message formatting (0) From There are 2147483647 users logged in now. To There are 2,147,483,647 users logged in now.

  33. Getting to advanced message formatting (1) Use a formatter logger: Logger logger = LogManager.getFormatterLogger(); logger.debug("There are %,d users logged in now.", () -> getUserCount());

  34. Getting to advanced message formatting (2) You can format numbers like this as well: logger.debug("Gain or loss: $ %(,.2f", delta); Gives you: Gain or loss: $ (12,582.19)

  35. Getting to advanced message formatting (3) You can get extra fancy with dates: Calendar c = new GregorianCalendar(1918, MAY, 11); logger.info("Richard Feynman's Birthday: %1$tb %1$te, %1$tY", c); Gives you: Richard Feynman's: May 11, 1918

  36. Noodle on that one for a minute How would you only log open and close door events using loggers and levels? You can assume that door events are logged at what? Maybe some appliances log door events at the INFO level and others at the DEBUG level. What if doors from a third party library log this kind of event at TRACE, another twist!

  37. Understanding Markers Instead of: logger.info("Door opened"); ... logger.info("Door closed"); Do this: logger.info(DOOR, "Door opened"); ... logger.info(DOOR, "Door closed"); Where DOOR is a constant that defines a marker: public static final Marker DOOR = MarkerManager.getMarker( DOOR );

  38. Configuring Makers (inclusive) <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN" monitorInterval="5"> <Appenders> <Console name="Console" target="SYSTEM_OUT" /> </Appenders> <Loggers> <Root level="WARN"> <AppenderRef ref="Console" /> </Root> <Logger name="com.example.oven" level="DEBUG" /> <Logger name="com.example.garage" level="INFO" /> </Loggers> <Filters> <MarkerFilter marker="DOOR" onMatch="ACCEPT" onMismatch="DENY" /> </Filters> </Configuration>

  39. Hiding Marked events If instead, we wanted to hide DOOR events, we d say: <MarkerFilter marker= DOOR" onMatch="DENY" onMismatch="NEUTRAL" />

  40. Marker usage examples I ve used markers for: Different kinds of hex dumps: Compressed buffers Decompressed buffers IO buffers Different kinds of SQL statements: Queries Batches Prepared statements Flow tracing (built-in Log4j) and more

  41. Coding Flow Tracing public String doIt() { logger.entry(); String testMsg = compute() return logger.exit(testMsg); }

  42. Configuring Flow Tracing <File name="log" fileName="target/test.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File>

  43. Reading Flow Tracing 19:08:07.060 TRACE com.test.TestService 38 doIt - entry parms(0) 19:08:07.060 TRACE com.test.TestService 42 doIt - exit with (Hello, World)

  44. Flow Tracing and Better Stack Traces 19:08:07.077 DEBUG com.test.TestService 32 exampleException - catching java.lang.ArrayIndexOutOfBoundsException: 3 at com.test.TestService.exampleException(TestService.java:29) [classes/:?] at com.test.App.main(App.java:9) [classes/:?] at com.test.AppTest.testApp(AppTest.java:15) [test-classes/:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.6.0_29] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[?:1.6.0_29] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[?:1.6.0_29] at java.lang.reflect.Method.invoke(Method.java:597) ~[?:1.6.0_29] at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99) [junit-4.3.1.jar:?] at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81) [junit-4.3.1.jar:?] at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) [junit-4.3.1.jar:?] at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75) [junit-4.3.1.jar:?] at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45) [junit-4.3.1.jar:?] at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66) [junit-4.3.1.jar:?]

  45. Flow Tracing Gotcha Class Method Line All comes from a stack snapshot

  46. Fish Tagging Many clients Many threads One logger per unique client

  47. Fish tagging with context stack ThreadContext.push(UUID.randomUUID().toString()); // Add the fishtag; logger.debug("Message 1"); ... logger.debug("Message 2"); ThreadContext.pop();

  48. Fish tagging with context map ThreadContext.put("id", UUID.randomUUID().toString(); // Add the fishtag; ThreadContext.put("ipAddress", request.getRemoteAddr()); ThreadContext.put("loginId", session.getAttribute("loginId")); ThreadContext.put("hostName", request.getServerName()); ... logger.debug("Message 1"); ... logger.debug("Message 2"); ... ThreadContext.clear();

  49. Fish tagging with pattern layout Use %X by itself to include the full contents of the Map. Use %X{key} to include the specified key. Use %x to include the full contents of the Stack.

Related


More Related Content