T-Racks - examples
T-Racks is a simple nice tool and framework to support analysis of execution paths and measurements, specifically in a multithreading environment. We can analyze an applications execution path and state at runtime as usual by debugging that application, but sometimes that is a cumbersome task to do, depending on the applications architecure and depending on our tooling. In most cases debugging is the best choice, but when it comes to concurrency, specifically with reused threads from thread pools managed by a JEE web container or EJB container or the like, it could leave us with a headache and lots of time spent fruitless. Many developers tend to catch the fish implementing lots of logging statements like mass fishing rods. That is a way to analyze an execution path and state at individually choosen points, independent from the runtime, but it is mixed up with all the other entries in the logfiles and there is a tendency among developers to not remove these logging statements after the analysis, because to distinguish these statements from permanently important ones we have to review them individually. Some developers pollute the code with statements to denote the start and the end of each and every method in the entire application, but that's rather the job for a framework that applies cross-cutting behavior (as with AOP) or for a tool that uses agents and extra classloaders to instrument the classes (as with classical tracing tools used on a production or test server).
T-Racks aims at local developer tests and test environments primarily and to provide a quick and easy way to output or retrieve tracing data from individually choosen points. Quick and easy because the most important tracing data are extracted and provided automatically and because simple to use. Individually choosen points, because we want to analyze specific matters and individual suspects and pass selected values to the tracing. Most of the times we have a clue about where to analyze and therefore its less helpful to get masses of tracing data or log entries and having to search for the needle in the haystack.
T-Racks can output tracing data in whatever format, filtering and sorting we want (we can extend that), but out of the box it provides a StackTracer, QueueTracer and a XMLTracer. With that we can output the tracing data in a CSV format in different sequences and in chunks, as well as output in an XML format for a better overview of execution paths. T-Racks supports also to apply tracing levels (not to be confused with log levels). The output is usually exported to a dedicated file, not the log files, or print to the console, but we can also just get the tracing data from the tracer for our own further processing. In order to get the output complete with multiple threads, which is especially important for the XML format, the tracers provide operations to await for all threads for individual tracks or the entire tracing. This way it works also well with unit tests where the main thread terminates before the tracing is completed.
Let's look at some usage examples. For instance in a foobar unit test we write...
StackTracer tracer = new StackTracer();
tracer.setTracerName("FOOBAR-TRACER");
tracer.setExportFile(new File(foobarTracingFile));
tracer.setCapacity(100, true, true);
Tracers.addTracer(tracer);
Foobar.doFoobarStuff();
tracer.awaitTotalCompletion(0, 100, TimeUnit.MILLISECONDS);
...to create a tracer that works on a stack, optionally give it a name to distinguish it later (we can employ several tracers at the same time), optionally set a file to output to, optionally set a capacity limit (to not run into memory issues) and indicate if a chunk shall be printed or exported before it is thrown away. Then we add the tracer to the Tracers and execute the unit to test (here Foobar). Eventually, with multiple threads, we have to wait with a delay and / or timeout for all threads before the main thread of the unit test should terminate.
In the units under test (here Foobar) we get first the tracer...
private static final Tracer<?> TRACER = Tracers.getTracer("FOOBAR-TRACER");
...then we add some tracing statements, optionally with trace levels, and optionally with as many extra arguments we like...
new Start(TRACER, TraceLevel.FINER, "Hello world!", foobar.toString()){};
...
new Trace(TRACER, TraceLevel.FINE){};
...
new End(TRACER, TraceLevel.FINER){};
...which is the same as writing this:
TRACER.trace(new Start(){}, TraceLevel.FINER, "Hello world!", foobar.toString());
...
TRACER.trace(new Trace(){}, TraceLevel.FINE);
...
TRACER.trace(new End(){}, TraceLevel.FINER);
As you can see we instantiate objects from inner anonymous classes which extend the abstract types Start, Trace and End. This supplies T-Racks automatically with several tracing data and has a way better performance (like any regular instruction) than other ways to get the same information (like stack traces or reflections). It just looks somewhat uggly, but you will get used to it ;-)
We can also derive our own types from Trace. For example to integrate the trace level, something like:
new FineTrace(TRACER){};
We can already run the unit test and we get a tracing.
Another point is how to assign newborn threads to a track. The safest is to get the track...
TracingEntry track = TracingContext.getInstance().getCurrentTrack(Thread.currentThread());
... and pass it to the new thread, where we set it as the current track:
TracingContext.getInstance().setCurrentTrack(Thread.currentThread(), track);
T-Racks provides also easier ways: T-Racks can "guess" the track. To support its "guessing" we can mark a trace as fork to indicate that a new thread will be born next, out of the current one:
new Trace(TRACER){}.markAsFork();
Same way we can denote the start of a new track:
new Trace(TRACER){}.startNewTrack();
In case we have want to add traces to tracks in places like proxies, AOP, instrumentation code or reflection code, it will come in handy that we can create a trace directly as a simple bean that we pass to the tracer:
TRACER.trace(new TraceData(tracedClass, tracedConstructor, tracedMethod));
We can even create an entire TracingEntry bean with all data if we gathered these data otherwise.
There is much more we can do with T-Racks - discover it!
More coming soon.
About the creator & author