16
16
////
17
17
= Flow Tracing
18
18
19
- The `Logger` class provides logging methods that are quite useful for
20
- following the execution path of applications. These methods generate
21
- logging events that can be filtered separately from other debug logging.
22
- Liberal use of these methods is encouraged as the output has been found
23
- to
24
-
25
- * aid in problem diagnosis in development without requiring a debug
26
- session
27
- * aid in problem diagnosis in production where no debugging is possible
28
- * help educate new developers in learning the application.
29
-
30
- The most used methods are the entry() or traceEntry() and exit() or
31
- traceExit() methods. entry() or traceEntry() should be placed at the
32
- beginning of methods, except perhaps for simple getters and setters.
33
- entry() can be called passing from 0 to 4 parameters. Typically these
34
- will be parameters passed to the method. traceEntry() can be passed a
35
- format String and a variable list of parameters, or a Message. The
36
- entry() and traceEntry() methods log with a level of TRACE and uses a
37
- Marker with a name of "ENTER" which is also a "FLOW" Marker and all
38
- message strings will begin with "event", even if a format String or
39
- Message is used.
40
-
41
- The main difference between the entry and traceEntry methods is that the
42
- entry method accepts a variable list of objects where presumably each is
43
- a method parameter. The traceEntry method accepts a format string
44
- followed by a variable list of objects, presumably included in the
45
- format String. It is not possible to have a single method that includes
46
- both of these as it would be ambiguous whether the first String is a
47
- parameter or a format String.
48
-
49
- An exit() or traceExit() method should be placed before any return
50
- statement or as the last statement of methods without a return. exit()
51
- and traceExit() can be called with or without a parameter. Typically,
52
- methods that return void will use exit() or traceExit() while methods
53
- that return an Object will use exit(Object obj) or traceExit(object, new
54
- SomeMessage(object)). The exit() and traceExit() methods log with a
55
- level of TRACE and uses a Marker with a name of "EXIT" which is also a
56
- "FLOW" Marker and all message strings will begin with "exit", even if a
57
- format String or Message is used.
58
-
59
- The throwing() method can be used by an application when it is throwing
60
- an exception that is unlikely to be handled, such as a RuntimeException.
61
- This will insure that proper diagnostics are available if needed. The
62
- logging event generated will have a level of ERROR and will have an
63
- associated Marker with a name of "THROWING" which is also an "EXCEPTION"
64
- Marker.
19
+ Flow tracing in Log4j is an advanced logging technique designed to enhance
20
+ the visibility of application processes. With this technique, developers can track
21
+ the flow of data through their application by using special methods that log entry
22
+ and exit points within the code.
65
23
66
- The catching() method can be used by an application when it catches an
67
- Exception that it is not going to rethrow, either explicitly or attached
68
- to another Exception. The logging event generated will have a level of
69
- ERROR and will have an associated Marker with a name of "CATCHING" which
70
- is also an "EXCEPTION" Marker.
24
+ These methods are:
71
25
72
- The following example shows a simple application using these methods in
73
- a fairly typical manner. The throwing() is not present since no
74
- Exceptions are explicitly thrown and not handled.
26
+ * `entry()` or `traceEntry()`
27
+ * `exit()` or `traceExit()`
28
+ * `throwing()`
29
+ * `catching()`
75
30
76
- [source,java]
77
- ----
78
- package com.test;
31
+ With these methods, we can investigate environments where traditional debugging is not possible,
32
+ such as in production or during live application monitoring.
33
+ Furthermore, new developers can be educated on the application's behavior by examining the logs.
79
34
80
- import org.apache.logging.log4j.Logger;
81
- import org.apache.logging.log4j.LogManager;
35
+ Flow tracing offers a structured approach to all this.
82
36
83
- import java.util.Random;
37
+ == Flow Tracing Methods
84
38
85
- public class TestService {
86
- private Logger logger = LogManager.getLogger(TestService.class.getName());
39
+ The methods used most often are `entry()` or `traceEntry()` and `exit()` or `traceExit()`.
40
+ As the name suggests, the "entry" methods are used at the beginning of a method,
41
+ while the "exit" methods are used at the end of a method.
87
42
88
- private String[] messages = new String[] {
89
- "Hello, World",
90
- "Goodbye Cruel World",
91
- "You had me at hello"
92
- };
93
- private Random rand = new Random(1);
43
+ [source, java]
44
+ ----
45
+ public void someMethod() {
46
+ logger.entry(); <1>
47
+ // method body
48
+ logger.exit(); <2>
49
+ }
50
+ ----
51
+ <1> The `entry()` method is called at the beginning of the method.
52
+ <2> The `exit()` method is called at the end of the method.
94
53
95
- public void setMessages(String[] messages) {
96
- logger.traceEntry(new JsonMessage(messages));
97
- this.messages = messages;
98
- logger.traceExit();
99
- }
54
+ Both `entry()` and `exit()` methods can be called with or without parameters.
55
+ In the case of `entry()` it makes sense to pass the method parameters as arguments.
100
56
101
- public String[] getMessages() {
102
- logger.traceEntry();
103
- return logger.traceExit(messages, new JsonMessage(messages));
104
- }
57
+ [source, java]
58
+ ----
59
+ public void someMethod(String param) {
60
+ logger.entry(param); <1>
61
+ // method body
62
+ logger.exit(); <2>
63
+ }
64
+ ----
65
+ <1> The `entry()` method is called at the beginning of the method.
105
66
106
- public String retrieveMessage() {
107
- logger.entry();
67
+ The `traceEntry()` also supports messages.
108
68
109
- String testMsg = getMessage(getKey());
69
+ [source, java]
70
+ ----
71
+ public void someMethod(String[] text) {
72
+ logger.traceEntry(new JsonMessage(text)); <1>
73
+ // method body
74
+ }
75
+ ----
76
+ <1> Using the `JsonMessage` class to log the `text` parameter.
110
77
111
- return logger.exit(testMsg);
112
- }
78
+ Very similar, it is possible to use `exit()` with methods that return a value.
113
79
114
- public void exampleException() {
115
- logger.entry();
116
- try {
117
- String msg = messages[messages.length];
118
- logger.error("An exception should have been thrown");
119
- } catch (Exception ex) {
120
- logger.catching(ex);
121
- }
122
- logger.exit();
123
- }
80
+ [source, java]
81
+ ----
82
+ public String someMethod() {
83
+ String result = "Hello";
84
+ // method body
85
+ return logger.exit(result); <1>
86
+ }
87
+ ----
88
+ <1> The `exit()` method can also return a value.
124
89
125
- public String getMessage(int key) {
126
- logger.entry(key);
90
+ To work with exceptions, the `catching()` and `throwing()` methods are used.
127
91
128
- String value = messages[key];
92
+ The following code shows, how to use the `catching()` method. It will be called
93
+ inside the `catch` block of a try-catch statement.
129
94
130
- return logger.exit(value);
131
- }
95
+ The `catching()` method can be used by an application when it catches an
96
+ Exception that it is not going to rethrow, either explicitly or attached
97
+ to another Exception. The logging event generated will have a level of `ERROR`.
132
98
133
- private int getKey() {
134
- logger.entry();
135
- int key = rand.nextInt(messages.length);
136
- return logger.exit(key);
99
+ [source, java]
100
+ ----
101
+ public void someMethod() {
102
+ try {
103
+ // Lets assume an exception is thrown here
104
+ String msg = messages[messages.length];
105
+ } catch (Exception ex) {
106
+ logger.catching(ex); <1>
137
107
}
138
108
}
139
109
----
110
+ <1> The `catching()` method is used to log exceptions that are caught and not rethrown.
140
111
141
- This test application uses the preceding service to generate logging
142
- events.
112
+ The `throwing()` method is used to log exceptions that are thrown and not caught.
113
+ The code shows how to use the `throwing()` method- like `catching()` it will be called
114
+ inside the `catch` block of a try-catch statement.
143
115
144
- [source,java]
145
- ----
146
- package com.test;
147
-
148
- public class App {
116
+ The `throwing()` method can be used by an application when it is throwing
117
+ an exception that is unlikely to be handled, such as a RuntimeException.
118
+ This will ensure that proper diagnostics are available if needed. The
119
+ logging event generated will have a level of `ERROR`.
149
120
150
- public static void main( String[] args ) {
151
- TestService service = new TestService();
152
- service.retrieveMessage();
153
- service.retrieveMessage();
154
- service.exampleException();
121
+ [source, java]
122
+ ----
123
+ public void someMethod() {
124
+ try {
125
+ // Lets assume an exception is thrown here
126
+ String msg = messages[messages.length];
127
+ } catch (Exception ex) {
128
+ logger.throwing(ex); <1>
155
129
}
156
130
}
157
131
----
132
+ <1> The `throwing()` method is used to log exceptions that are thrown and not caught.
133
+
134
+ == Differences in flow tracing methods
135
+
136
+ Flow tracing methods have specific markers assigned and logs with a level of `TRACE`.
137
+ It's also noteworthy that all messages will begin with the word "event".
138
+
139
+ The table below shows the methods and their special features.
140
+
141
+ [cols="3,3,3", options="header"]
142
+ |===
143
+ | Method Name | Marker Name | Special Features
144
+
145
+ | `entry()`
146
+ | `ENTER`, `FLOW`
147
+ | Accepts 0 to 4 parameters
148
+
149
+ | `traceEntry()`
150
+ | `ENTER`, `FLOW`
151
+ | Can take a format string and a variable list of parameters.
152
+
153
+ | `exit()`
154
+ | `EXIT`, `FLOW`
155
+ | Can be called with or without parameters.
156
+
157
+ | `traceExit()`
158
+ | `EXIT`, `FLOW`
159
+ | Handles return values differently based on the method signature.
160
+
161
+ | `throwing()`
162
+ | `THROWING`, `EXCEPTION`
163
+ | Typically used when an application throws an exception that is unlikely to be handled, such as a RuntimeException.
164
+
165
+ | `catching()`
166
+ | `CATCHING`, `EXCEPTION`
167
+ | Used when catching exceptions that are not rethrown; logs with ERROR level.
168
+
169
+ |===
170
+
171
+ == Flow Tracing Example Configuration
172
+
173
+ The following example demonstrates how to configure Log4j to use flow tracing.
174
+ While it is recommended to use the `JsonTemplateLayout` in production, we are using the
175
+ `PatternLayout` in this example for simplicity.
158
176
159
177
The configuration below will cause all output to be routed to
160
178
target/test.log. The pattern for the FileAppender includes the class
161
- name, line number and method name. Including these in the pattern are
179
+ name, line number and method name. Including these in the pattern is
162
180
critical for the log to be of value.
163
181
182
+ The following example demonstrates how you could use flow tracing.
183
+ The Configuration element is set with a status of "error," which means Log4j
184
+ will only report issues that are of error severity or higher.
185
+ Within the Appenders section, two appenders are defined: Console and File.
186
+
187
+ The Console appender is configured to output logs to `SYSTEM_OUT`, typically the console.
188
+ It includes a `ThresholdFilter` set to only accept messages at the `ERROR` level or above.
189
+ This filters out less severe messages.
190
+ The output format is specified by a `PatternLayout`, designed to include detailed
191
+ trace information such as time, log level, class name, line number, and method name.
192
+ Please note, that we are recommending `JsonTemplateLayout` over `PatternLayout` in production.
193
+
194
+ Similarly, the File appender directs logs to a file named `target/test.log`.
195
+ The appenders configuration will create a new file for every application run.
196
+
197
+ Finally, in the Loggers section, the Root logger is set to a `TRACE` level which is necessary
198
+ to see flow tracing in action. The Root logger references the File appender, directing
199
+ its output to the configured file.
200
+
164
201
[source,xml]
165
202
----
166
203
<?xml version="1.0" encoding="UTF-8"?>
@@ -184,88 +221,4 @@ critical for the log to be of value.
184
221
</Configuration>
185
222
----
186
223
187
- Here is the output that results from the Java classes and configuration
188
- above.
189
-
190
- ....
191
- 19:08:07.056 TRACE com.test.TestService 19 retrieveMessage - entry
192
- 19:08:07.060 TRACE com.test.TestService 46 getKey - entry
193
- 19:08:07.060 TRACE com.test.TestService 48 getKey - exit with (0)
194
- 19:08:07.060 TRACE com.test.TestService 38 getMessage - entry parms(0)
195
- 19:08:07.060 TRACE com.test.TestService 42 getMessage - exit with (Hello, World)
196
- 19:08:07.060 TRACE com.test.TestService 23 retrieveMessage - exit with (Hello, World)
197
- 19:08:07.061 TRACE com.test.TestService 19 retrieveMessage - entry
198
- 19:08:07.061 TRACE com.test.TestService 46 getKey - entry
199
- 19:08:07.061 TRACE com.test.TestService 48 getKey - exit with (1)
200
- 19:08:07.061 TRACE com.test.TestService 38 getMessage - entry parms(1)
201
- 19:08:07.061 TRACE com.test.TestService 42 getMessage - exit with (Goodbye Cruel World)
202
- 19:08:07.061 TRACE com.test.TestService 23 retrieveMessage - exit with (Goodbye Cruel World)
203
- 19:08:07.062 TRACE com.test.TestService 27 exampleException - entry
204
- 19:08:07.077 DEBUG com.test.TestService 32 exampleException - catching java.lang.ArrayIndexOutOfBoundsException: 3
205
- at com.test.TestService.exampleException(TestService.java:29) [classes/:?]
206
- at com.test.App.main(App.java:9) [classes/:?]
207
- at com.test.AppTest.testApp(AppTest.java:15) [test-classes/:?]
208
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.6.0_29]
209
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[?:1.6.0_29]
210
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[?:1.6.0_29]
211
- at java.lang.reflect.Method.invoke(Method.java:597) ~[?:1.6.0_29]
212
- at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99) [junit-4.3.1.jar:?]
213
- at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81) [junit-4.3.1.jar:?]
214
- at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) [junit-4.3.1.jar:?]
215
- at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75) [junit-4.3.1.jar:?]
216
- at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45) [junit-4.3.1.jar:?]
217
- at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66) [junit-4.3.1.jar:?]
218
- at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35) [junit-4.3.1.jar:?]
219
- at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42) [junit-4.3.1.jar:?]
220
- at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) [junit-4.3.1.jar:?]
221
- at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52) [junit-4.3.1.jar:?]
222
- at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35) [surefire-junit4-2.7.2.jar:2.7.2]
223
- at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:115) [surefire-junit4-2.7.2.jar:2.7.2]
224
- at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97) [surefire-junit4-2.7.2.jar:2.7.2]
225
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.6.0_29]
226
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[?:1.6.0_29]
227
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[?:1.6.0_29]
228
- at java.lang.reflect.Method.invoke(Method.java:597) ~[?:1.6.0_29]
229
- at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103) [surefire-booter-2.7.2.jar:2.7.2]
230
- at $Proxy0.invoke(Unknown Source) [?:?]
231
- at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:150) [surefire-booter-2.7.2.jar:2.7.2]
232
- at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:91) [surefire-booter-2.7.2.jar:2.7.2]
233
- at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69) [surefire-booter-2.7.2.jar:2.7.2]
234
- 19:08:07.087 TRACE com.test.TestService 34 exampleException - exit
235
- ....
236
-
237
- Simply changing the root logger level to DEBUG in the example above will
238
- reduce the output considerably.
239
-
240
- ....
241
- 19:13:24.963 DEBUG com.test.TestService 32 exampleException - catching java.lang.ArrayIndexOutOfBoundsException: 3
242
- at com.test.TestService.exampleException(TestService.java:29) [classes/:?]
243
- at com.test.App.main(App.java:9) [classes/:?]
244
- at com.test.AppTest.testApp(AppTest.java:15) [test-classes/:?]
245
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.6.0_29]
246
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[?:1.6.0_29]
247
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[?:1.6.0_29]
248
- at java.lang.reflect.Method.invoke(Method.java:597) ~[?:1.6.0_29]
249
- at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99) [junit-4.3.1.jar:?]
250
- at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81) [junit-4.3.1.jar:?]
251
- at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) [junit-4.3.1.jar:?]
252
- at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75) [junit-4.3.1.jar:?]
253
- at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45) [junit-4.3.1.jar:?]
254
- at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66) [junit-4.3.1.jar:?]
255
- at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35) [junit-4.3.1.jar:?]
256
- at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42) [junit-4.3.1.jar:?]
257
- at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) [junit-4.3.1.jar:?]
258
- at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52) [junit-4.3.1.jar:?]
259
- at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35) [surefire-junit4-2.7.2.jar:2.7.2]
260
- at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:115) [surefire-junit4-2.7.2.jar:2.7.2]
261
- at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97) [surefire-junit4-2.7.2.jar:2.7.2]
262
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.6.0_29]
263
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[?:1.6.0_29]
264
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[?:1.6.0_29]
265
- at java.lang.reflect.Method.invoke(Method.java:597) ~[?:1.6.0_29]
266
- at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103) [surefire-booter-2.7.2.jar:2.7.2]
267
- at $Proxy0.invoke(Unknown Source) [?:?]
268
- at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:150) [surefire-booter-2.7.2.jar:2.7.2]
269
- at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:91) [surefire-booter-2.7.2.jar:2.7.2]
270
- at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69) [surefire-booter-2.7.2.jar:2.7.2]
271
- ....
224
+ By changing the level of the Root logger to `DEBUG`, you can reduce the amount of output.
0 commit comments