© Excellent backgrounds/Shutterstock.com
ELK jenseits der HTTP-Access-Logs

Elchtest


Der ELK Stack – Elasticsearch in Kombination mit Kibana und logstash – ist zurzeit in aller Munde. Neben dem Auswerten von Webserverlogs können auch für uns Entwickler interessante Informationen direkt aus der Java-Anwendung gewonnen werden.

logstash ist ein Werkzeug, mit dem sich Ereignisse aus beliebigen Quellen extrahieren, manipulieren und persistieren lassen. In der Regel wird logstash die Daten aus den Log-Dateien lesen, diese filtern und routen und dann in jeweilige Elasticsearch-Indizes speichern. Die Ereignisse können beliebig viele (auch verschachtelte) Eigenschaften haben; die wichtigste Eigenschaft ist allerdings der Zeitstempel, der angibt, wann das Ereignis auf dem Quellsystem auftrat.

Liegen die Ereignisse erst einmal in Elasticsearch, kann man mit Kibana sehr einfach grafische Dashboards aufbauen. Kibana ist ein ausgezeichnetes Mittel, um schnell einen Überblick zu bekommen und dann beliebige interaktive Drilldowns zu ermöglichen. Treten zum Beispiel auf einmal vermehrt Fehlermeldungen auf, kann Kibana zur Analyse der Ursache eingesetzt werden. Ob nun vermehrt Traffic vom falschen Load Balancer (falsche Quell-IP-Adresse), Hackversuche (Methodenaufrufe mit vielen verschiedenen Parametern) oder ein streikendes Backend (Exceptions aus dem JDNC Stack) die Ursache ist: Durch exploratives Filtern auf die gespeicherten Ereignisse ist schnell die Ursache für den Ausschlag gefunden. In diesem Artikel zeigen wir, mit welchen Schritten und mit welch geringem Aufwand eine typische Java-Webanwendung mit ELK verbunden werden kann.

Selbst probieren

Begleitend zum Artikel wurden alle Quellen inklusive der Vagrant-Boxen über [1] online gestellt. In der README.md sind alle notwendigen Schritte für die Verwendung beschrieben. Die Boxen sind sehr sparsam konfiguriert worden. Damit läuft das Beispiel auch auf älteren Maschinen, wenn auch zum Teil langsam.

Spring Pet Clinic

Spring bietet eine Demoanwendung an, von der Sie sicherlich schon gehört haben: Spring Pet Clinic (Abb. 1). Auch für uns wird das die Ausgangsbasis für die Demonstration sein [2]. Die Demo wird mit dem Logging-Framework Logback geliefert. Dieses muss im Vorfeld durch Log4j ersetzt werden.

siprell_1.tif_fmt1.jpgAbb. 1: Screenshot der Pet-Clinic-Anwendung

Java Logging

Es gibt unterschiedlichste Methoden, die zu beobachtenden Ereignisse in logstash zu schreiben. Wir haben uns entschieden, die Ereignisse per Log4j und mithilfe des entsprechenden Appenders in Log-Dateien zu schreiben und diese per Forwarder über das Netzwerk an eine zentrale logstash-Instanz zu schicken. Hierfür muss in der Regel in produktiven Umgebungen am wenigsten geändert werden. Typische Log-Statements sind:

LOG.info("Backend result received") LOG.warn("Received Error, trying something else") LOG.fatal("This should never happen")

In der Regel findet man Log-Statements wie in Listing 1 im Legacy-Code der Anwendung. Beim Schreiben des Quelltexts hat sich der Entwickler bestimmt etwas gedacht, aber spätestens in der Produktion fragt sich der Betrieb: Welches Ergebnis habe ich von welchem System bekommen, welchen Fehler habe ich erhalten, was versuche ich als Nächstes, und was darf nie passieren?

Damit die gesammelten Aussagen interpretiert werden können, müssen wir diese stärker strukturieren und mehr Kontext mitliefern. Fangen wir mit dem Strukturieren an.

Das globale Anpassen von Programmstrukturen orthogonal zur Geschäftslogik ist die Standarddomäne der aspektorientierten Programmierung – AOP. In unserem Beispiel möchten wir alle RequestMapping-Methoden der Spring MVC Controller protokollieren. Da in der Spring Pet Clinic alle Controller per Annotation deklariert werden, nutzen wir das in Listing 1 gezeigte Konstrukt in unserer Annotation.

Listing 1: Verwendung von AOP zur Protokollierung der Aufrufe aller „RequestMapping“-Methoden

private Logger apiLogger = LoggerFactory.getLogger("API"); @Pointcut("within(@org.springframework.stereotype.Controller *)") public void controllerBean() {} @Pointcut("execution(* *(..))") public void methodPointcut() {} @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public void requestMapping() {} @Around("controllerBean() && methodPointcut() && requestMapping()") public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint) throws Throwable { [...] apiLogger.info("called: {}", joinPoint.getSignature()); return joinPoint.proceed(); }

Damit erhalten wir Ausgaben, die wie folgt aussehen:

called: ModelAndView org.springframework.[...].showOwner(int)Ende

Diese Ausgaben protokollieren alle Aufrufe auf der API-Kategorie. Allerdings fehlt uns hier noch der Kontext. Es ist somit nicht mögl...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang