Titel-Logo
TraceLogger
Abstrakt
Beispiele
Manuelle Nutzung
Zugriff via Name
Zugriff via Thread
Zugriff via Queue
Techniken
Javadoc
UML-Klassendiagramm
Download
Basics of Cryptography
Custom JBossAS Login
SOAP Webservice
Role Based Access Control

Obgleich es möglich ist durch Konstruktor-Aufrufe entsprechende Tracer-Objekte zu erzeugen, sollte man üblicherweise hiervon absehen, da auf diese Weise die Nutzung spezieller Tracer "hart kodiert" wird. Dennoch kann man an diesem Beispiel einige der Grundprinzipien bei der Verwendung der Tracer erkennen:

1 final AbstractTracer tracer = new FileTracer("Example");
2 tracer.open();
3 try {
4 class Foo {
5 void bar() {
6 tracer.entry("void", this, "bar()");
7 try {
8 tracer.out().printfIndentln("This is an example.");
9 }
10 finally {
11 tracer.wayout();
12 }
13 }
14 }
15 Foo foo = new Foo();
16 foo.bar(); // nothing will be printed because no tracing context has been provided
17 tracer.initCurrentTracingContext(2, true);
18 foo.bar(); // this generates output
19 }
20 finally {
21 tracer.close();
22 }

In Zeile 1 wird ein FileTracer erzeugt. Der Name eines Tracer wird bei FileTracern als Logfilename wiederverwendet. Die open()-Methode in Zeile 2 wird demnach versuchen im Verzeichnis ./log das File Example.log anzulegen wobei das Verzeichnis ./log existieren muss. Mittels setLogDirPath(...) kann diese Voreinstellung überschrieben werden. Es empfiehlt sich das zugehörige close() - wie dargestellt - in einem finally-Block unterzubringen. In diesem Beispiel möchten wir die Methode bar() der Klasse Foo beobachten. Hierzu dienen die entry(...)- und wayout()-Aufrufe in den Zeilen 6 und 11. Es ist von entscheidender Bedeutung, dass jedes entry(...) mit einem wayout() in einem finally-Block wieder geschlossen wird. Der Aufruf foo.bar() in Zeile 16 wird keine Ausgabe zur Folge haben da für den ausführenden Thread noch kein Tracing-Kontext zur Verfügung steht. Zeile 17 erzeugt nun einen Tracing-Kontext der besagt, dass wir für diesen Thread bis zu einer Stapeltiefe von 2 an Ausgaben interessiert sind. Nachdem der Tracer wieder geschlossen wurde, enthält das File Example.log nachstehendes Tracing:

1 --> TraceLog opened!
2 Time     : Mi Apr 02 23:14:41 MESZ 2014
3 Bufsize  : 512
4 Autoflush: true
5
6 ENTRY--void Foo[12275192].bar()--main[1]
7 This is an example.
8 RETURN-void Foo[12275192].bar()--(+0ms)--(+0ms)--main[1]
9
10 --> TraceLog closing!
11 Time     : Mi Apr 02 23:14:41 MESZ 2014

In den Zeilen 1 bis 4 ist vermerkt worden zu welcher Zeit und mit welchen Einstellungen der Tracer geöffnet wurde. In Zeile 6 ist notiert, dass die Methode bar() der Klasse Foo vom main-Thread mit der Id 1 aufgerufen wurde. Die Foo-Instanz hatte den System-Identity-Hashcode 12275192. Entsprechendes sehen wir in Zeile 8 als die Methode die Steuerkontrolle wieder abgegeben hatte. Es ist keine messbare Zeit zwischen Aufruf und Verlassen der Methode verstrichen.

[Oben]

Beispiel - Zugriff per Name

Bei Verwendung der TracerFactory können Tracer per Namen angefordert werden. Die entsprechende Methode gibt einen (polymorphen) AbstractTracer zurück. Ohne den Code zu ändern kann der Typ des Tracers via Konfiguration ausgetauscht werden. Statt ins lokale Filesystem zu tracen, kann bspw. völlig transparent auch übers Netzwerk an ein Logging-Server gesendet werden.

1 <?xml version="1.0" encoding="UTF-8" ?>
2 <TraceConfig xmlns="http://www.christofreichardt.de/java/tracer">
3 <Pool>
4 <TraceLogger name="ExampleTracer" class="de.christofreichardt.diagnosis.file.FileTracer">
5 <LogDir>./log</LogDir>
6 <AutoFlush>true</AutoFlush>
7 <BufSize>1024</BufSize>
8 <Limit>1048576</Limit>
9 <Context>
10 <Thread name="main">
11 <Online>true</Online>
12 <DebugLevel>5</DebugLevel>
13 </Thread>
14 </Context>
15 </TraceLogger>
16 </Pool>
17 </TraceConfig>

Beginnend mit Zeile 4 wird in obiger Konfiguration ein FileTracer mit Namen "ExampleTracer" definiert. Dessen zugehöriges Logfile wird ./log/ExampleTracer.log sein. Jedesmal wenn eine Methode wieder vom Stapel geholt wird, wird der PrintStream des Tracers "geflusht" werden. Nach etwa einem MebiByte (= 1024*1024 Byte) wird ein Backup des Logfile durchgeführt. Der Tracer ist an Ausgaben vom main-Thread bis zu einer Stapeltiefe von 5 interessiert. Weitere Tracer sind im Pool nicht definiert. Diese Konfiguration kann unter der Annahme, dass sie im File ./config/ExampleConfig.xml abgelegt ist, wie folgt genutzt werden:

1 File configFile = new File("." + File.separator + "config" + File.separator + "ExampleConfig.xml");
2 TracerFactory.getInstance().readConfiguration(configFile);
3 final AbstractTracer tracer = TracerFactory.getInstance().getTracer("ExampleTracer");
4 tracer.open();
5 try {
6 class Foo {
7 final AbstractTracer tracer;
8 public Foo() throws TracerFactory.Exception {
9 this.tracer = TracerFactory.getInstance().getTracer("ExampleTracer"); // throws an Exception if "ExampleTracer" doesn't exist.
10 }
11 void bar() {
12 this.tracer.entry("void", this, "bar()");
13 try {
14 this.tracer.out().printfIndentln("Within bar().");
15 baz();
16 }
17 finally {
18 this.tracer.wayout();
19 }
20 }
21 void baz() {
22 this.tracer.entry("void", this, "baz()");
23 try {
24 this.tracer.out().printfIndentln("Within baz().");
25 }
26 finally {
27 this.tracer.wayout();
28 }
29 }
30 }
31 Foo foo = new Foo();
32 foo.bar(); // nothing will be printed because no tracing context has been provided
33 tracer.initCurrentTracingContext(); // the configured tracing context will be used
34 foo.bar(); // this generates output
35 }
36 finally {
37 tracer.close();
38 }

Dies wird zu folgender Ausgabe führen:

1 --> TraceLog opened!
2 Time     : Mi Apr 02 23:14:41 MESZ 2014
3 Bufsize  : 1024
4 Autoflush: true
5
6 ENTRY--void Foo[668710].bar()--main[1]
7 Within bar().
8 ENTRY--void Foo[668710].baz()--main[1]
9 Within baz().
10 RETURN-void Foo[668710].baz()--(+0ms)--(+0ms)--main[1]
11 RETURN-void Foo[668710].bar()--(+0ms)--(+0ms)--main[1]
12
13 --> TraceLog closing!
14 Time     : Mi Apr 02 23:14:41 MESZ 2014

Diese Vorgehensweise ist sinnvoll wenn man einzelne Klassen in einer Single-Thread-Anwendung analysieren möchte. In einer Multi-Thread-Umgebung würden Tracing-Ausgaben hier nur erfolgen wenn die betreffenden Code-Teile vom main-Thread ausgeführt werden. Es ist möglich einen Tracer für mehrere Threads zu registrieren. Dann muss man aber die Zugriffe auf den Ausgabestream via tracer.getSyncObject() selbst synchronisieren. Stattdessen sind die nachstehenden Verfahren anzuraten:

[Oben]

Diese Vorgehensweise empfiehlt sich für Multi-Thread-Anwendungen bei denen man die Kontrolle über die Thread-Erzeugung hat. Für jeden Thread wird dann ein Tracer definiert auf den man via TracerFactory.getInstance().getCurrentPoolTracer() zugreifen kann. Bei der Erzeugung der Threads ist darauf zu achten, dass diese eindeutige Namen erhalten. Intern werden die Tracer durch eine Map<Long, AbstractTracer> organisiert. Vor der Programmausführung existieren jedoch noch keine Thread-Objekte (oder Thread-Ids) und deswegen muss die Assoziation zwischen Thread und Tracer in der Konfiguration über den Namen erfolgen:

1 <?xml version="1.0" encoding="UTF-8" ?>
2 <TraceConfig xmlns="http://www.christofreichardt.de/java/tracer">
3 <Pool>
4 <TraceLogger name="WorkerTracer-0" class="de.christofreichardt.diagnosis.file.FileTracer">
5 <LogDir>./log</LogDir>
6 <AutoFlush>true</AutoFlush>
7 <BufSize>1024</BufSize>
8 <Limit>1048576</Limit>
9 <Context>
10 <Thread name="Worker-0">
11 <Online>true</Online>
12 <DebugLevel>5</DebugLevel>
13 </Thread>
14 </Context>
15 </TraceLogger>
16 <TraceLogger name="WorkerTracer-1" class="de.christofreichardt.diagnosis.file.FileTracer">
17 <LogDir>./log</LogDir>
18 <AutoFlush>true</AutoFlush>
19 <BufSize>1024</BufSize>
20 <Limit>1048576</Limit>
21 <Context>
22 <Thread name="Worker-1">
23 <Online>true</Online>
24 <DebugLevel>5</DebugLevel>
25 </Thread>
26 </Context>
27 </TraceLogger>
28 <TraceLogger name="WorkerTracer-2" class="de.christofreichardt.diagnosis.file.FileTracer">
29 <LogDir>./log</LogDir>
30 <AutoFlush>true</AutoFlush>
31 <BufSize>1024</BufSize>
32 <Limit>1048576</Limit>
33 <Context>
34 <Thread name="Worker-2">
35 <Online>true</Online>
36 <DebugLevel>5</DebugLevel>
37 </Thread>
38 </Context>
39 </TraceLogger>
40 </Pool>
42 <Map>
43 <Threads>
44 <Thread name="Worker-0">
45 <TraceLogger ref="WorkerTracer-0"/>
46 </Thread>
44 <Thread name="Worker-1">
45 <TraceLogger ref="WorkerTracer-1"/>
46 </Thread>
44 <Thread name="Worker-2">
45 <TraceLogger ref="WorkerTracer-2"/>
46 </Thread>
47 </Threads>
47 </Map>
41 </TraceConfig>

In obiger Konfiguration definieren wir drei Tracer (WorkerTracer-0, WorkerTracer-1, WorkerTracer-2) die an Ausgaben dreier Worker-Threads (Worker-0, Worker-1, Worker-2) interessiert sind. Außerdem definieren wir eine Abbildung zwischen Thread und Tracer (Worker-0 => WorkerTracer-0, Worker-1 => WorkerTracer-1, Worker-2 => WorkerTracer-2). Nachstehendes Code-Snippet verdeutlicht wie obige Konfiguration ausgenutzt werden kann:

1 class Foo implements Callable<Integer>, Traceable {
2 final int id;
3 public Foo(int id) {
4 this.id = id;
5 }
6 @Override
7 public Integer call() throws java.lang.Exception {
8 AbstractTracer tracer = getCurrentTracer();
9 tracer.initCurrentTracingContext();
10 tracer.entry("Integer", this, "call()");
11 try {
12 tracer.logMessage(LogLevel.INFO, "Consumer(" + this.id + ")", getClass(), "call()");
13 bar();
14 return this.id;
15 }
16 finally {
17 tracer.wayout();
18 }
19 }
20 void bar() {
21 getCurrentTracer().entry("void", this, "bar()");
22 try {
23 getCurrentTracer().out().printfIndentln("Within bar().");
24 baz();
25 }
26 finally {
27 getCurrentTracer().wayout();
28 }
29 }
30 void baz() {
31 getCurrentTracer().entry("void", this, "baz()");
32 try {
33 getCurrentTracer().out().printfIndentln("Within baz().");
34 }
35 finally {
36 getCurrentTracer().wayout();
37 }
38 }
39 @Override
40 public AbstractTracer getCurrentTracer() {
41 return TracerFactory.getInstance().getCurrentPoolTracer();
42 }
43 }
44 File configFile = new File("." + File.separator + "config" + File.separator + "ExampleConfig.xml");
45 TracerFactory.getInstance().readConfiguration(configFile);
46 TracerFactory.getInstance().openPoolTracer();
47 try {
48 final int THREAD_NUMBER = 3;
49 final int ITERATIONS = 100;
50 final int TIMEOUT = 5;
51 List<Future<Integer>> futures = new ArrayList<>();
52 ExecutorService executorService = Executors.newFixedThreadPool(THREAD_NUMBER, new ThreadFactory() {
53 AtomicInteger threadNr = new AtomicInteger();
54 @Override
55 public Thread newThread(Runnable r) {
56 return new Thread(r, "Worker-" + this.threadNr.getAndIncrement());
57 }
58 });
59 try {
60 for (int i = 0; i < ITERATIONS; i++) {
61 futures.add(executorService.submit(new Foo(i)));
62 }
63 try {
64 int i = 0;
65 for (Future<Integer> future : futures) {
66 assert future.get() == i++;
67 }
68 }
69 catch (ExecutionException ex) {
70 ex.getCause().printStackTrace(System.err);
71 }
72 }
73 finally {
74 executorService.shutdown();
75 assert executorService.awaitTermination(TIMEOUT, TimeUnit.SECONDS);
76 }
77 }
78 finally {
79 TracerFactory.getInstance().closePoolTracer();
80 }

Zunächst definieren wir die Klasse Foo welches die Callable<V>-Schnittstelle implementiert. Auf diese Weise können Instanzen von Foo einem ExecutorService zur nebenläufigen Ausführung übergeben werden. Zusätzlich implementiert die Klasse Foo die Schnittstelle Traceable. Über die Indirektion getCurrentTracer() kann die Konfiguration überschrieben werden. Schreiben wir in Zeile 41 etwa return TracerFactory.getInstance().getDefaultTracer(); statt return TracerFactory. getInstance().getCurrentPoolTracer(); so wird für das Tracing ein NullTracer verwendet der lediglich das Logstatement in Zeile 12 an den JDK-Logger weiterleitet und ansonsten alle Tracing-Ausgaben verwirft. Ebenso wird die Methode getCurrentPoolTracer() der TracerFactory uns einen NullTracer liefern falls kein Tracer für den gegenwärtigen Thread definiert ist. Einen ExecutorService besorgen wir uns über die Executors-Factory in Zeile 52. Dieser ExecutorService arbeitet mit drei Threads die wir mit einer eigenen ThreadFactory erzeugen. So können wir den Threads die gewünschten Namen (und ggf. weitere Eigenschaften) verleihen. Als nächstes bekommt ExecutorService der Reihe nach 100 Foo-Instanzen zur Ausführung. Welcher der drei Threads welchen Auftrag bekommt ist nicht-deterministisch, d.h. die Ausgaben der 100 Foo-Instanzen werden sich mehr oder weniger unvorhersehbar auf die drei Logs aufteilen - allerdings werden Ausgaben die aus dem Worker-0-Thread getätigt wurden immer im Log des WorkerTracer-0 landen, usw. Einen Auszug der Tracing-Ausgaben aus dem Log des WorkerTracer-0 sehen Sie nachstehend:

1 --> TraceLog opened!
2 Time     : Fr Apr 11 16:43:52 MESZ 2014
3 Bufsize  : 1024
4 Autoflush: true
5
6 ...
28 ENTRY--Integer Foo[14919523].call()--Worker-0[38]
29 ********
30 * INFO * [Fr Apr 11 16:43:52 MESZ 2014] [38,Worker-0] [de.christofreichardt.diagnosis.ExampleUnit$2Foo] "Consumer(28)"
31 ********
32 ENTRY--void Foo[14919523].bar()--Worker-0[38]
33 Within bar().
34 ENTRY--void Foo[14919523].baz()--Worker-0[38]
35 Within baz().
36 RETURN-void Foo[14919523].baz()--(+0ms)--(+0ms)--Worker-0[38]
37 RETURN-void Foo[14919523].bar()--(+0ms)--(+0ms)--Worker-0[38]
38 RETURN-Integer Foo[14919523].call()--(+0ms)--(+0ms)--Worker-0[38]
39 ...
248
249 --> TraceLog closing!
250 Time     : Fr Apr 11 16:43:52 MESZ 2014

Manchmal haben wir jedoch die Thread-Erzeugung nicht wie in diesem Beispiel unter Kontrolle. Dann sind wir weder in der Lage die Eindeutigkeit der Thread-Namen zu garantieren noch kennen wir diese vor Ausführung der Anwendung und können deshalb auch kein Mapping von Thread(name) auf Tracer definieren. In diesem Fall kann die Tracer-Queue benutzt werden, siehe nachstehendes Beispiel:

[Oben]

Java EE-Anwendungen laufen unter der Kontrolle eines Java EE Anwendungsservers der selbst Thread-Pools verwaltet um eingehende Anforderungen zu bearbeiten oder Nachrichten weiterzuleiten. Obgleich es in gewissem Umfang möglich sein kann die Thread-Pools in unserem Sinne zu konfigurieren, ist dies manchmal nicht ausreichend. In solchen Fällen definieren wir eine Queue mit uniformen Tracern aus der wir uns bedienen wenn wir einen Request nachverfolgen wollen. Die Queue wird blockieren wenn kein Tracer mehr vorrätig ist. Dies beschränkt die Anzahl der Requests die der Java EE Anwendungsserver parallel bearbeiten kann ungeachtet der Größe seiner Thread-Pools. Wird die letzte Methode aus dem Anwendungsstapel eines Tracers wieder herausgenommen, wird der gegenwärtige Tracing-Kontext gelöscht und der Tracer wird automatisch an die Queue zurückgegeben. Betrachten wir zunächst nachstehende Konfiguration einer Queue mit fünf Tracern:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <TraceConfig xmlns="http://www.christofreichardt.de/java/tracer">
3 <Pool/>
4 <Queue>
5 <Enabled/>
6 <Size>5</Size>
7 <TraceLogger name="QueueTracer" class="de.christofreichardt.diagnosis.file.QueueFileTracer">
8 <LogDir>${catalina.base}/logs</LogDir>
9 <AutoFlush>true</AutoFlush>
10 <BufSize>1024</BufSize>
11 </TraceLogger>
12 <Online>true</Online>
13 <DebugLevel>3</DebugLevel>
14 </Queue>
15 </TraceConfig>

Beachten Sie die Vereinbarung bzgl. des Log-Verzeichnisses in Zeile 8. Da die Beispielanwendung unter Verwendung eines Tomcat 7 entwickelt wurde, benutzen wir dessen catalina.base-System-Eigenschaft um das logs-Verzeichnis der Tomcat-Distribution zu referenzieren. Unter der Annahme, dass diese Konfiguration der Anwendung als Resource dem Package de.christofreichardt.infraservlet beiliegt, können wir nachstehenden WebFilter implementieren:

1 @WebFilter(filterName = "MyTraceFilter", servletNames = {"MyTracingServlet"}, dispatcherTypes = {DispatcherType.REQUEST})
2 public class MyTraceFilter implements Filter {
3 private FilterConfig filterConfig;
4 @Override
5 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
6 AbstractTracer tracer = TracerFactory.getInstance().takeTracer();
7 tracer.initCurrentTracingContext();
8 tracer.entry("void", this, "doFilter(ServletRequest request, ServletResponse response, FilterChain chain)");
9 try {
10 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
11 tracer.out().printfIndentln("method = %s", httpServletRequest.getMethod());
12 chain.doFilter(request, response);
13 }
14 finally {
15 tracer.wayout();
16 }
17 }
18 @Override
19 public void init(FilterConfig filterConfig) {
20 this.filterConfig = filterConfig;
21 InputStream resourceAsStream = MyTraceFilter.class.getClassLoader().getResourceAsStream("/de/christofreichardt/infraservlet/TraceConfig.xml");
22 if (resourceAsStream != null) {
23 try {
24 try {
25 TracerFactory.getInstance().reset();
26 TracerFactory.getInstance().readConfiguration(resourceAsStream);
27 TracerFactory.getInstance().openQueueTracer();
28 }
29 catch (TracerFactory.Exception ex) {
30 this.filterConfig.getServletContext().log("MyTraceFilter:Problems when evaluating the configuration resource: " + ex.getMessage());
31 }
32 }
33 finally {
34 try {
35 resourceAsStream.close();
36 }
37 catch (IOException ex) {
38 }
39 }
40 }
41 else
42 this.filterConfig.getServletContext().log("MyTraceFilter:No resource found.");
43 }
44 @Override
45 public void destroy() {
46 TracerFactory.getInstance().closeQueueTracer();
47 }
48 }

In Zeile 21 besorgen wir uns durch die init()-Methode des Servlets die XML-Konfigurations-Resource als InputStream und initialisieren die TracerFactory entsprechend. Anschließend werden die Ausgabestreams der (Queue-)Tracer geöffnet. Umgekehrt werden beim Herunterfahren der Tomcat-Servlet-Engine - wenn die destroy()-Methode aufgerufen wird - die Ausgabestreams der Tracer wieder geschlossen, siehe Zeile 46. Die eigentliche Nachverfolgung des Requests findet in der doFilter()-Methode statt. Zunächst besorgen wir uns in Zeile 6 einen Tracer aus der Queue. Die Methode takeTracer() wird blockieren falls momentan kein Tracer vorrätig ist. Anschließend müssen wir einen aktuellen Tracing-Kontext erzeugen. Dann legen wir ein Bild der doFilter()-Methode auf den Aufrufstapel dieses Tracing-Kontextes. Da die doFilter()-Methode den Anfang des Callstacks bzgl. des Requests definiert wird umgekehrt in Zeile 15 nicht nur das Bild der doFilter()-Methode wieder vom Stapel genommen sondern zugleich auch der Tracing-Kontext gelöscht und der Tracer an die Queue zurückgegeben.

Dieser WebFilter ist nachstehendem Servlet mit dem Namen "MyTracingServlet" vorgeschaltet. Die Idee ist es, dass wir das Tracing ausschalten können, einfach indem wir das "MyTracingServlet" aus der servletNames-Vereinbarung des obigen WebFilters in Zeile 1 herausnehmen. Es werden dann lediglich Log-Meldungen an den JDK-Logger weitergereicht.

1 @WebServlet(name = "MyTracingServlet", urlPatterns = {"/MyTracingServlet"})
2 public class MyTracingServlet extends HttpServlet implements Traceable {
3 private static final AtomicInteger REQUEST_COUNTER = new AtomicInteger();
4 protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
5 AbstractTracer tracer = getCurrentTracer();
6 int requestId = REQUEST_COUNTER.incrementAndGet();
7 tracer.logMessage(LogLevel.INFO, "Request-Id: " + requestId, getClass(), "doFilter");
8 tracer.entry("void", this, "processRequest(HttpServletRequest request, HttpServletResponse response)");
9 try {
10 response.setContentType("text/html;charset=UTF-8");
11 try (PrintWriter out = response.getWriter()) {
12 out.println("<!DOCTYPE html>");
13 out.println("<html>");
14 out.println("<body>");
15 out.println("<h1>Servlet MyTracingServlet at " + request.getContextPath() + "</h1>");
16 out.println("</body>");
17 out.println("</html>");
18 }
19 }
20 finally {
21 tracer.wayout();
22 }
23 }
24 @Override
25 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
26 processRequest(request, response);
27 }
28 @Override
29 public AbstractTracer getCurrentTracer() {
30 return TracerFactory.getInstance().getCurrentQueueTracer();
31 }
32 }

Das Servlet implementiert wieder die Traceable-Schnittstelle. So können wir die Konfiguration ggf. in Zeile 30 überschreiben. Die Methode processRequest() bearbeitet die GET-Anforderung. In Zeile 5 besorgen wir uns (nicht-blockierend) den Tracer für den Thread der dem Request zugeteilt wurde. Die Zeilen 3, 6 und 7 zählen die Anforderungen und loggen die entsprechende Request-Id. Bei aktivem Trace-Filter sieht die Nachverfolgung eines Request wie unten gezeigt aus:

1 ...
2 ENTRY--void MyTraceFilter[10555327].doFilter(ServletRequest request, ServletResponse response, FilterChain chain)--http-bio-8080-exec-2[21]
3 method = GET
4 ********
5 * INFO * [Mi Apr 23 00:05:32 MESZ 2014] [21,http-bio-8080-exec-2] [de.christofreichardt.infraservlet.MyTracingServlet] [doFilter] "Request-Id: 18"
6 ********
7 ENTRY--void MyTracingServlet[13630782].processRequest(HttpServletRequest request, HttpServletResponse response)--http-bio-8080-exec-2[21]
8 RETURN-void MyTracingServlet[13630782].processRequest(HttpServletRequest request, HttpServletResponse response)--(+0ms)--(+0ms)--http-bio-8080-exec-2[21]
9 RETURN-void MyTraceFilter[10555327].doFilter(ServletRequest request, ServletResponse response, FilterChain chain)--(+0ms)--(+0ms)--http-bio-8080-exec-2[21]
10 ...

Nun ist auch die Zuordnung zwischen Tracer und Thread nicht-deterministisch. Schalten wir jetzt das Tracing für das Servlet aus - durch entsprechende Konfiguration des WebFilters - wird lediglich die Log-Meldung aus Zeile 7 an den JDK-Logger weitergereicht. Geeignete logging.properties vorausgesetzt, könnte das konventionelle Logging dann so aussehen:

1 ...
2 [Mi Apr 23 00:16:41 MESZ 2014] [INFORMATION] [de.christofreichardt.infraservlet.MyTracingServlet] [doFilter] "Request-Id: 1"
3 [Mi Apr 23 00:16:41 MESZ 2014] [INFORMATION] [de.christofreichardt.infraservlet.MyTracingServlet] [doFilter] "Request-Id: 5"
4 [Mi Apr 23 00:16:41 MESZ 2014] [INFORMATION] [de.christofreichardt.infraservlet.MyTracingServlet] [doFilter] "Request-Id: 8"
5 [Mi Apr 23 00:16:41 MESZ 2014] [INFORMATION] [de.christofreichardt.infraservlet.MyTracingServlet] [doFilter] "Request-Id: 6"
6 [Mi Apr 23 00:16:41 MESZ 2014] [INFORMATION] [de.christofreichardt.infraservlet.MyTracingServlet] [doFilter] "Request-Id: 9"
7 ...

[Oben]

Valid XHTML 1.0 Strict