Wo sinnvoll habe ich ThreadLocals verwendet um Threads auf Tracer bzw. Tracing-Kontexte abzubilden. In einem Fall hätte dies jedoch gegenüber
einer ConcurrentMap<Thread,AbstractTracer>
keine Vorteile eingebracht und die Komplexität des Codes eher erhöht.
Um Konfigurationen on-the-fly ändern zu können, sind wo nötig Read-Write-Locks eingesetzt worden.
Die XML-Konfiguration wird gegen ein XML-Schema validiert bevor sie wirksam werden kann. Die Unittests via JUnit 4 benutzen diverse
ExecutorService
s und
Callable<V>
-Implementierungen um nebenläufige Szenarios darzustellen. Der Build erfolgte mit Maven.
Ein wesentlicher Bestandteil des Konzeptes ist der der Polymorphismus der Tracer. Die Basisklasse AbstractTracer
definiert dabei das grundlegende Verhalten der Tracer. Die FileTracer
und NetTracer
unterscheiden sich nur hinsichtlich der Datensenke ihrer Ausgabeströme. Der NullTracer
redefiniert den vom
AbstractTracer
geerbten Kontrakt in seinem Sinne, indem er die geerbten Methoden mit leeren Methodenrümpfen
überschreibt die allenfalls null
oder einen NullPrintStream
zurückgeben. Der
NullPrintStream
überschreibt seinerseits die vom PrintStream
geerbten Methoden
mit leeren Methodenrümpfen. Der finale JDKLoggingRouter
ist ein NullTracer
der lediglich
konventionelle Log-Meldungen an den JDKLogger weiterreicht. Die Java Virtual Machine kann diese leeren Methoden durch
Inlining eliminieren, so dass die Auswirkungen auf die Performance in der Produktion bei ausgeschaltetem Tracing gering sind.
Betrachten Sie bspw. nachstehendes Codesnippet:
1 |
TracerFactory.getInstance().reset(); |
2 |
AbstractTracer tracer = TracerFactory.getInstance().getCurrentPoolTracer(); |
3 |
BigInteger sum = new BigInteger("0"); |
4 |
for (long i=0; i<1000*1000*1000; i++) { |
5 |
sum = sum.add(BigInteger.valueOf(i)); |
6 |
// tracer.out().printfIndentln("sum = %d", sum); |
7 |
} |
8 |
System.out.printf("sum = %d%n", sum); |
Wird die Zeile 6 aktiviert, hat dies bei Verwendung der Server VM keinen messbaren Einfluss auf die Laufzeit, da die Defaultkonfiguration der
TracerFactory
bei Aufruf von getCurrentPoolTracer()
immer einen
NullTracer
liefert der seinerseits immer via
NullPrintStream
alle Ausgaben verwirft.
Neben den grundlegenden Ausprägungen FileTracer
, NetTracer
und
NullTracer
gibt es noch zwei weitere Tracer-Typen die von AbstractTracer
ableiten, den
DebugLogTee<T extends AbstractTracer>
und den
QueueTracer<T extends AbstractTracer>
. Ersterer definiert eine Abzweigung die zusätzlich Log-Meldungen
an ein (konventionelles) Logging-Framework weiterreicht und letzterer einen Tracer der von einer (blockierenden) Queue angefordert werden kann.
Hier kommt jeweils ein Polymorphismus durch Komposition zum Einsatz. Einerseits redefinieren diese beiden Tracer-Typen den geerbten Kontrakt,
andererseits soll es bspw. auch einen OueueFileTracer
geben der in ein File schreibt. Da Java keine
Mehrfachvererbung kennt, delegieren diese Tracer die eigentliche Arbeit an eine interne Instanz des Template-Arguments.