Richtiges Logging innerhalb der Anwendung ist eine Kunst für sich. Das Logging an sich sollte minimal-invasiv sein, so dass der Entwickler in der normalen Programmiertätigkeit nicht eingeschränkt oder behindert wird. Das Logging sollte zentral definiert und vor allem auch zentral konfigurierbar sein.
Eine Möglichkeit Logging zu realisieren ist die Nutzung der
aspektorientierten Programmierung. Dabei wird der bestehende Code weitesgehend unangetastet gelassen und nur eine weitere Ebene über den bestehenden Code gelegt. Die funktionalen Aufgaben (
Core-Level-Concerns) und damit gleichzeitig die Kernaufgaben der Software bleiben im Mittelpunkt und werden nicht mit den bestehenden technischen Randbedingungen (
System-Level-Concerns) vermischt.
Gerade
Spring bietet mit der einfachen Integration von
AspectJ eine einfach zu konfigurierende Möglichkeit ein systemweites, losgelöstes Logging zu realisieren. Und genau um dieses Thema, visualisiert an einem kleinen Beispiel, soll es in diesem Eintrag gehen. Wie immer gilt, dass die Ausschnitte vergrößert werden können, sollten diese zu klein sein.
Um den Overhead zur Erstellung eines Projektes so klein wie möglich zu gestalten, wird komplett auf eine Verwaltung der Dependencies durch
Maven gesetzt. Daher an dieser Stelle nur die Erläuterung der berücksichtigten Dependencies.
Für die Basiskonfiguration von Spring wird die folgende Dependency benötigt.
|
Spring-Context Dependency |
|
|
|
|
Sollte diese in dem Standard-Repository von Maven nicht vorhanden sein, so kann auf das folgende Repository ausgewichen werden.
|
Alternatives Repository für Spring Dependencies | |
Für die Nutzung von AspectJ werden zwei Dependencies benötigt. Zum einen AspectJ selbst, aber zum anderen auch
CGLIB zur dynamischen Erstellung von Bytecode zur Laufzeit des Programmes.
|
AspectJ und CGLIB Dependencies |
|
Das Beispiel selbst soll nachher durch einen Test gestartet und damit auch direkt getestet werden. Dafür werden sowohl Spring-Test (inkl.
TestNG) als auch
JUnit benötigt.
|
Hinzufügen von JUnit und Spring-Test |
|
Die Ausgaben auf der Konsole erfolgen dann via
Log4J und nicht über
System.out oder
System.err.
|
Log4j Dependency |
Nach einem initialen Build des Projekts und dem Download der Bibliotheken sollten diese innerhalb des Maven Projektes vorhanden sein.
|
Dependencies im Projekt |
Der eigentliche Aufbau des Beispiels ist einfach gehalten, damit die wesentlichen Punkte besser zur Geltung kommen. Das Beispiel besteht aus einer Spring Bean mit implementiertem Interface, der AOP-Komponente, einer Testklasse und den Konfigurationsdateien.
|
Klassendiagramm für das Beispiel |
|
Die Konfiguration wird, soweit möglich, direkt über Annotationen innerhalb der Klassen realisiert. Um einen Einstieg in das Beispiel zu finden, zuerst die Erläuterung der Implementierung der HelloWorldComponent.
|
HelloWorldComponent Interface |
Die eigentliche Implementierung ist ebenfalls einfach gehalten und Bedarf keiner weiteren Erläuterung.
|
Implementierung der Komponente |
Diese einfache Implementierung bildet die Basis für das Beispiel und wird durch den aspektorientierten Logger vervollständigt. Dieser wird innerhalb der Klasse AopMethodLogger implementiert.
|
Aspektorientierter Logger auf Methodenlevel |
Der Logger wird durch die Annotation
@Component Spring Komponente definiert, so dass eine Initialisierung durch das Spring Framework gegeben ist. Damit der Logger gleichzeitig als Aspekt bekannt ist, wird zusätzlich die Annotation
@Aspect verwendet, die ebenfalls durch Spring ausgewertet wird. Die eigentliche Methode für das Loggen kann frei definiert werden. In diesem Fall wird der Methode eine Instanz eines
ProceedingJoinPoints übergeben, damit auf Eigenschaften der Methode zugegriffen und die Methode nach dem Loggen weiter ausgeführt werden kann. Damit die Methode überhaupt als Aspekt um eine aufgerufene Methode definiert wird, muss mit der Annotation
@Around ein
Pointcut gekennzeichnet werden. Zusätzlich wird ein Pattern für den Pointcut definiert, damit festgelegt werden kann, wann in diesen Pointcut verzweigt werden soll. In diesem Fall definiert der Ausdruck "
execution(* de.sado.examples..*.*(..))" die Ausführung einer jeden Methode innerhalb des Packages
de.sado.examples, egal wie die Signatur der Methode definiert ist. Eine sehr gute Beschreibung der weiteren Möglichkeiten findet sich in der
offiziellen Referenz zur aktuellen Version von Spring.
Neben der Implementierung der Klassen ist auch die Konfiguration von Spring relevant. Durch die Annotations-gestützte Implementierung bleibt die erforderliche Konfigurationsdatei allerdings recht schlank.
|
Spring Konfigurationsdatei |
Neben der Bean Definition der HelloWorldComponent finden sich hier auch die Scan-Anweisung für das Scannen nach Komponenten und die Konfiguration der AOP Nutzung. Weitere Konfigurationsschritte sind nicht mehr notwendig.
Für den Start des kleinen Beispiel eignet sich ein durch den
SpringJUnit4ClassRunner gesteuerter Test. Der Runner garantiert, dass ein interner IoC Container mitgestartet wird und so keine Spring Referenzen gemockt oder per Hand aufgelöst werden müssen. Es kann sogar die Konfiguration der Anwendung wiederverwendet werden, wenn sich dies ergibt.
|
Test der erstellten Komponente |
Durch die Annotation
@ContextConfiguration wird die Konfigurationsdatei referenziert. Die Annotation
@RunWith sorgt für die Ausführung des Tests mit Hilfe des Spring-internen Testrunners. Somit müssen die Tests nur noch per Maven gestartet werden und schon sieht man das Ergebnis auf der Kommandozeile.
Das Beispiel kann bei
Cloudbees ausgecheckt werden. Dort befindet sich ein fertiges Eclipse Projekt mit den entsprechenden Einstellungen.