Einer der gebräuchlichen Ansätze, um Daten in einen ElasticSearch-Cluster zu importieren, ist Logstash. In verschiedenen Situationen, z. B. wenn keine explizite technische Infrastruktur aufgebaut werden soll, um Nachrichten aus einer einzelnen Quelle zu verarbeiten, ist diese Kombination nicht immer sinnvoll.
Als Alternative wird im Folgenden ein einfacher Appender für das u. a. von Spring-boot standardmäßig verwendete Logging-Framework logback vorgestellt, der ohne den Umweg über einen Logstash-Import und Elasticsearch-Export direkt auf die Elasticsearch-REST-Schnittstelle zugreift und die Logdaten übermittelt.
Benötigte Abhängigkeiten
Die Maven-Artifact-IDs der benötigten Abhängigkeiten sind:
– spring-web für die Klasse RestTemplate zum Verschicken von HTTP-POST-Nachrichten
– logstash-logback-encoder zur Umwandlung von ILoggingEvent aus Logback in von Elasticsearch nutzbares JSON
Statt spring-web kann natürlich auch eine andere Bibliothek zum Senden von HTTP-POST-Nachrichten, z. B. Apache HttpClient, verwendet werden.
Konfiguration von Logback
Die Einbindung des Appenders erfolgt klassisch über einen entsprechenden Eintrag in die Datei logback.xml durch:
<
appender
name
=
"ES_APPENDER"
class
=
"ElasticSearchAppender"
>
</
appender
>
...
<
root
level
=
"DEBUG"
>
<
appender-ref
ref
=
"ES_APPENDER"
/>
<
appender-ref
ref
=
"CONSOLE"
/>
</
root
>
Implementierung des Appenders
Der Appender wird von AppenderBase abgeleitet und ermöglicht damit das Behandeln von ILoggingEvents. Jeder zu loggende Event wird über den Logstash-Encoder in ein elasticsearch-kompatibles JSON-Format überführt und anschließend an die im Appender konfigurierte URL per REST gePOSTed.
public
class
ElasticSearchAppender
extends
AppenderBase<ILoggingEvent> {
private
RestTemplate template =
new
RestTemplate();
private
String url;
private
ThreadLocal<Encoder<ILoggingEvent>> encoder;
public
ElasticSearchAppender() {
encoder =
new
ThreadLocal<Encoder<ILoggingEvent>>() {
@Override
protected
Encoder<ILoggingEvent> initialValue() {
LogstashEncoder encoder =
new
LogstashEncoder();
encoder.start();
return
encoder;
}
};
}
@Override
protected
void
append(ILoggingEvent eventObject) {
String postData =
null
;
try
{
ByteArrayOutputStream os =
new
ByteArrayOutputStream();
encoder.get().init(os);
encoder.get().doEncode(eventObject);
postData = os.toString();
}
catch
(IOException e) {
// Wird bewusst ignoriert.
return
;
}
// Sende Event an ES.
postToElasticSearch(url, postData);
}
private
void
postToElasticSearch(String url, String postData) {
try
{
template.postForLocation(
new
URI(url), postData);
}
catch
(RestClientException | URISyntaxException e) {
// Wird bewusst ignoriert.
}
}
public
String getUrl() {
return
url;
}
public
void
setUrl(String url) {
this
.url = url;
}
}