Logstash (Teil des Elastic Stack) ist eine Datenverarbeitungspipeline, mit der Sie Daten aus verschiedenen Quellen sammeln, sie dynamisch transformieren und an Ihr gewünschtes Ziel senden können. Es wird am häufigsten als Datenpipeline für Elasticsearch verwendet, eine Open-Source-Analyse- und Suchmaschine. Wenn Ihre Daten jedoch bereits im JSON-Format vorliegen, ist es nicht notwendig, Logstash zu verwenden. Durch die Umwandlung des Standard-Log4j-Formats von WSO2 in das JSON-Logformat können Sie Logstash vollständig umgehen. Sie können einfach Protokolle an Filebeat senden, der sie dann in Elasticsearch speichert.
Log4J und Log4J2 sind de facto Standard-Frameworks zum Protokollieren von Anwendungsnachrichten in Java. Grundsätzlich fügen Sie beim Entwickeln von Java-Anwendungen mehrere Anweisungen hinzu, die Informationen entsprechend der festgelegten Ebene protokollieren. Log4j ist auch der Standardanwendungsprotokollmechanismus für WSO2-Container. Hier ist ein großartiger Artikel unseres Kollegen Rob Blaauboer, wenn Sie mehr über die Interaktion von LOG4J2 und WSO2 erfahren möchten.
Wenn Sie ELK-Stack verwenden, um Daten aus verschiedenen Quellen in verschiedenen Formaten zu veröffentlichen und zu durchsuchen, zu analysieren und zu visualisieren, und nicht in der Lage sind, Logstash so zu konfigurieren, dass die Protokollzeilen aufgrund der Einschränkungen Ihres Projekts in JSON-Strings konvertiert werden, oder wenn Sie unnötige Serialisierungs-/Deserialisierungsvorgänge vermeiden möchten, die ressourcenintensiv sein können, möchten Sie Logstash möglicherweise umgehen und Ihre Protokollausgabe stattdessen im JSON-Logformat konvertieren.
Erstellen eines benutzerdefinierten JSON-Logmediators
Zuerst, wenn wir Eigenschaften bereitstellen und sie als separate Schlüssel/Wert-Paare anzeigen möchten, benötigen wir unseren eigenen Logmediator, da der eingebaute Mediator die Eigenschaften folgendermaßen festlegt:
private void setCustomProperties(StringBuffer sb, MessageContext synCtx) {
if (properties != null && !properties.isEmpty()) {
for (MediatorProperty property : properties) {
if(property != null){
sb.append(separator).append(property.getName()).append(" = ").append(property.getValue()
!= null ? property.getValue() :
property.getEvaluatedExpression(synCtx));
}
}
}
}
Bei Untersuchung des eingebauten Protokollmediators von WSO2 sehen wir, dass die vom Mediator bereitgestellten Eigenschaften im folgenden Format protokolliert werden:
Trennzeichen (Standard ist ‘,’) propertyName = propertyValue
Anstatt StringBuilder zu verwenden, was es uns schwerer machen würde, JSON-Objekte zu erstellen, verwenden wir eine HashMap und ordnen die Eigenschaften dieser Map zu, die wir erstellen.
private Map getCustomLogMessage(MessageContext synCtx) {
Map<String,String> map = new HashMap<>();
setCustomProperties(map, synCtx);
return map;
}
private void setCustomProperties(Map<String,String> map, MessageContext synCtx) {
if (properties != null && !properties.isEmpty()) {
for (MediatorProperty property : properties) {
if(property != null){
map.put(property.getName(), property.getValue() != null ? property.getValue() : property.getEvaluatedExpression(synCtx));
}
}
}
}
Da die Methode public void auditLog(Object msg) der Klasse MediatorLog im unten stehenden Code die Nachricht formatiert, indem sie die Methode getFormattedLog aus der Klasse LoggingUtils aufruft,
/**
* Log a message at level INFO to all available/enabled logs.
*/
public void auditLog(Object msg) {
String formattedMsg = LoggingUtils.getFormattedLog(synCtx, msg);
defaultLog.info(formattedMsg);
if (synCtx.getServiceLog() != null) {
synCtx.getServiceLog().info(msg);
}
if (traceOn) {
traceLog.info(formattedMsg);
}
}
werden wir die Nachricht in ThreadContext platzieren oder die Methode info (oder eine andere Stufe) der Klasse org.apache.log4j.Logger direkt aufrufen, ohne das Protokoll zu formatieren.
switch (category) {
case CATEGORY_INFO :
//synLog.auditLog(getLogMessage(synCtx));
ThreadContext.putAll(getLogMessage(synCtx));
log.info(new ObjectMessage(getLogMessage(synCtx)));
break;
case CATEGORY_ERROR :
//synLog.auditError(getLogMessage(synCtx));
ThreadContext.putAll(getLogMessage(synCtx));
log.error(new ObjectMessage(getLogMessage(synCtx)));
break;
}
//Clear the map!
ThreadContext.clearMap();
synLog.traceOrDebug("End : Log mediator");
Wenn Sie die Nachricht im Thread-Kontext platzieren, stellen Sie sicher, dass Sie die Map löschen. Wenn Sie dies nicht tun, können alte Eigenschaften aus verschiedenen Protokollaktionen in neuen Protokollereignissen angezeigt werden. Dies liegt an der Natur des MDC-Mechanismus, der von ThreadContext verwendet wird.
Wir werden ein OSGi-Bundle mit Factory- und Serializer-Klassen erstellen, der benutzerdefinierte Mediator-QName ist jsonLog für diesen Fall, was der benutzerdefinierte Mediatorname ist, den wir verwenden werden, wenn wir diesen Mediator aufrufen.
Anschließend werden wir die .jar-Datei in den Ordner {EI_HOME}/dropins verschieben.
Konfigurieren der Datei log4j.properties (Enterprise Integrator 6.x.x)
Der zweite Schritt besteht darin, zu konfigurieren, wie Ihre Protokolle formatiert werden sollen. Es gibt viele Layout-Bibliotheken verfügbar. Für dieses Projekt haben wir JSONEventLayoutV1 verwendet. Kompilieren Sie den Quellcode und verschieben Sie ihn in den Ordner {EI_HOME}/lib. Anschließend können Sie die Datei log4j.properties konfigurieren. Beispiel:
log4j.appender.CARBON_CONSOLE=org.wso2.carbon.utils.logging.appenders.CarbonConsoleAppender
log4j.appender.CARBON_CONSOLE.layout=net.logstash.log4j.JSONEventLayoutV1
Beispiel für die Verwendung des benutzerdefinierten Protokollmediators
Hier ist unten ein Beispiel, wie unser neuer benutzerdefinierter Protokollmediator verwendet wird. Die Verwendung ist ähnlich dem eingebauten Protokollmediator. Wir werden einige Eigenschaften wie Korrelations-ID, Nachrichten-ID, Namen der Sequenz, Quelle der Nachricht, Ziel der Nachricht, eine kurze Beschreibung der API und die Nutzlast selbst bereitstellen; nur um zu testen, ob das JSON-Objekt ordnungsgemäß erstellt wird.
<jsonLog level="custom">
<property expression="$func:correlationid" name="correlationid"/>
<property expression="$func:message" name="messageid"/>
<property expression="$func:sequence" name="sequence"/>
<property expression="$func:source" name="source"/>
<property expression="$func:destination" name="destination"/>
<property expression="$func:description" name="description"/>
<property expression="$ctx:current_body" name="payload"/>
</jsonLog>
Diese Konfigurationen geben das folgende JSON-Protokoll auf der Konsole aus:
{
"@timestamp": "2022-11-21T03:59:00.542Z",
"ecs.version": "1.2.0",
"log.level": "INFO",
"message": "{sequence=Generic_FaultSeq_v1, payload=<jsonObject><key1>value1</key1><key2>value2</key2></jsonObject>, destination=null, messageid=null, description=Start fault processing, correlationid=null, source=null}",
"process.thread.name": "PassThroughMessageProcessor-1",
"log.logger": "nl.rijksweb.mediator.JsonLogMediator",
"labels": {
"Correlation-ID": "2b530f10-e410-4cc7-878a-5dd42793aac2",
"bundle.id": "255",
"bundle.name": "synapse-core",
"bundle.version": "2.1.7.wso2v271",
"correlationid": "1",
"description": "Start fault processing",
"destination": "some other system",
"messageid": "1",
"payload": "<jsonObject><key1>value1</key1><key2>value2</key2></jsonObject>",
"sequence": "Generic_FaultSeq_v1",
"source": "some system"
}
}
Konfigurieren der log4j2.properties-Datei (Micro Integrator)
Genau wie bei Enterprise Integrator müssen wir auch beim Micro Integrator konfigurieren, wie Ihre Protokolle formatiert werden sollen.
Mit den Änderungen an den Abhängigkeiten und dem Carbon Kernel Logging Framework funktionierte das Herunterladen eines JSON-Layouts von Drittanbietern für MI4.1.0 nicht. In diesem Fall hat das Upgrade der pax-Logging-Version von wso2v4 auf wso2v5 den Trick gemacht, da wso2v5 log4j-layout-template-json und wso2v4 nicht enthält. Sie können die Jar-Dateien pax-logging-api-2.1.0-wso2v5 und pax-logging-log4j2-2.1.0-wso2v5 herunterladen und in den Ordner {EI_HOME}/dropins verschieben.
Beispiel für die Datei log4j2.properties:
appender.CARBON_CONSOLE.type = Console
appender.CARBON_CONSOLE.name = CARBON_CONSOLE
appender.CARBON_CONSOLE.layout = JsonTemplateLayout
appender.CARBON_CONSOLE.layout.eventTemplateUri = classpath:EcsLayout.json
appender.CARBON_CONSOLE.layout.locationInfoEnabled = true
appender.CARBON_CONSOLE.filter.threshold.type = ThreshholdFilter
appender.CARBON_CONSOLE.filter.threshold.level = DEBUG
Mit diesen Konfigurationen rufen wir den jsonLog-Mediator auf, den wir gerade erstellt haben, und dieser Aufruf gibt das folgende JSON-Protokoll auf der Konsole aus:
{
"@timestamp": "2022-11-21T03:59:00.542Z",
"ecs.version": "1.2.0",
"log.level": "INFO",
"message": "{sequence=Generic_FaultSeq_v1, payload=<jsonObject><key1>value1</key1><key2>value2</key2></jsonObject>, destination=null, messageid=null, description=Start fault processing, correlationid=null, source=null}",
"process.thread.name": "PassThroughMessageProcessor-1",
"log.logger": "nl.rijksweb.mediator.JsonLogMediator",
"labels": {
"Correlation-ID": "2b530f10-e410-4cc7-878a-5dd42793aac2",
"bundle.id": "255",
"bundle.name": "synapse-core",
"bundle.version": "2.1.7.wso2v271",
"correlationid": "null",
"description": "Start fault processing",
"destination": "null",
"messageid": "null",
"payload": "<jsonObject><key1>value1</key1><key2>value2</key2></jsonObject>",
"sequence": "Generic_FaultSeq_v1",
"source": "null"
}
}
Fazit
Protokollierung ist äußerst wichtig für alle Unternehmenssoftwareanwendungen und WSO2 ist keine Ausnahme. Das Protokollieren der Informationen vom Zeitpunkt des Eintreffens im System über den Mediationsfluss, den sie durchläuft, bis hin zum Verlassen des Systems kann den Systemadministratoren und Entwicklern wichtige Informationen liefern.
Die Verwendung von Tools wie dem ELK-Stack aggregiert alle Ihre Protokolle aus verschiedenen Quellen an einem einzigen zentralen Ort, was im Falle der Fehlersuche sehr hilfreich sein wird. Die Implementierung kann jedoch ihre eigenen Herausforderungen mit sich bringen.
Mit dem benutzerdefinierten jsonLog-Mediator können Sie gültige JSON-Objekte als Konsolen- (oder Datei-)Ausgabe ausgeben und Logstash umgehen, um unnötige Serialisierungs-/Deserialisierungsvorgänge zu vermeiden, die ressourcenintensiv sein können.
Besonderer Dank gilt Philip Akyempon und Steve Liem für ihre Unterstützung.
Ich hoffe aufrichtig, dass Ihnen diese Informationen bei Ihren Projekten helfen, und wenn Sie Fragen haben, zögern Sie bitte nicht, uns zu kontaktieren.