In die Azure-Cloud loggen

Übertragung und Auswertung von Anwendungslogs in den Azure-Log-Analytics-Arbeitsbereich
20
Oct

In die Azure-Cloud loggen

Sei es bei Amazon (AWS) oder bei Microsoft (Azure), Cloud-Lösungen haben sich bewährt, und so wagen immer mehr Unternehmen den Schritt in die Wolken. In diesem Artikel lernen wir drei verschiedene Wege kennen, wie wir Anwendungslogs in Azure mit dem Log-Analytics-Arbeitsbereich-Dienst sammeln und auswerten können, und was es dabei zu beachten gibt.

Cloud-Dienste stehen in unterschiedlichen Modellen zur Verfügung. Neueinsteiger, die bereits eine etablierte Anwendungslandschaft mit verschiedenen Applikationen haben, beginnen dabei oft mit dem Infrastructure-as-a-Service-Modell (kurz IaaS). Bei diesem Ansatz werden die eigenen Anwendungen zu in der Cloud gehosteten virtuellen Maschinen (nachfolgend VM genannt) migriert. Nach der Migration möchte man sichergehen, dass sich das System wie erwartet verhält. Ein bekanntes Mittel zur Prüfung der Systemgesundheit sind die Anwendungslogs. Sofern diese nicht mit Fehlermeldungen befüllt werden, gehen wir davon aus, dass das System einwandfrei ausgeführt wird.

Mein Kollege Roland Krummenacher hat in einem früheren Artikel [1] bereits einen Weg aufgezeigt, wie Logdaten in die Azure Cloud gesammelt werden können. Dazumal wurde eine Lösung unter der Verwendung des Azure-Storage-Diensts erläutert. In diesem Artikel möchte ich Ihnen den aktuellen Ansatz unter der Verwendung des Azure-Log-Analytics-Arbeitsbereich-Diensts vorstellen.

 

 

Daten sammeln

Ausgangslage für die Codebeispiele in diesem Artikel bildet ein Legacy-System bestehend aus einer oder mehreren .NET-Anwendungen, das auf VMs in der Azure Cloud migriert wird, und dessen Daten wir nun in Azure sammeln und auswerten möchten. Als Logging-Framework setzen wir hier auf log4net [2]. Listing 1 zeigt eine log4net-Konfiguration, mit der die Anwendungslogs in Textdateien geschrieben werden. Eine Logmeldung besteht in der Regel aus verschiedenen Attributen, die sich für die textuelle Ausgabe als Layout konfigurieren lassen. Für unser Beispiel beschränken wir uns auf die Attribute Datum (%date), Level (%level), Logger (%logger) und Meldung (%message).

Listing 1:
<log4net>
  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <file value="example.log" />
    <appendToFile value="true" />
    ...
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date %-5level - %logger - %message%newline" />
    </layout>
  </appender>
  <root>
    <appender-ref ref="RollingFile" />
  </root>
</log4net>

Und so sieht es anschließend in den Logdateien aus:

2021-04-26 14:07:04,146 INFO  - StartUpLogger - Service Started
2021-04-26 14:04:48,843 INFO  - StartUpLogger - Service Stopped
2021-04-26 14:23:25,971 ERROR - StartUpLogger - The service terminated abnormally
2021-04-26 14:23:37,744 FATAL - StartUpLogger - The service did not start successfully

Der Inhalt dieser Logdateien soll nun inAzurein einen Log-Analytics-Arbeitsbereich übertragen werden. Der Log-Analytics-Arbeitsbereich-Dienst speichert die Daten in sogenannte Protokolle. Für ein besseres Verständnis, was Protokolle sind, können Datenbanktabellen für einen Vergleich herangezogen werden. Indiesem Kontext wäre ein Log-Analytics-Arbeitsbereich eine Datenbank, und die Protokolle wären die Tabellen. Zur besseren Auswertung der gesammelten Daten empfiehlt es sich, die Anwendungsdaten in benutzerdefinierte Protokolle zu schreiben [3]. Auf YouTube finden sich viele gute Schritt-für-Schritt-Anleitungen, wie ein Log-Analytics-Arbeitsbereich samt benutzerdefiniertem Protokoll aufgesetzt werden kann [4]. In unserem Beispiel kreieren wir einen Log-Analytics-Arbeitsbereich mit der Bezeichnung „MyLogAnalyticsWorkspace“ und legen ein eigenes, benutzerdefiniertes Protokoll namens „MyLog_CL“ an (Abb. 1).

 

Abb. 1: Log-Analytics-Arbeitsbereich mit benutzerdefiniertem Protokoll

 

Variante 1: Log Analytics Agent

Eine Möglichkeit, die Anwendungslogs mit wenig Aufwand zu sammeln und an den Log-Analytics-Arbeitsbereich zu übermitteln, bietet der Log Analytics Agent (auch bekannt als Microsoft Monitoring Agent, kurz MMA) [5]. Der MMA kann als VM Extension für Azure VMs über das Azure Portal aktiviert und so auf der VM installiert werden. Danach wird der MMA als Windows-Dienst auf dem installierten Windows-System ausgeführt. Beim Einrichten des benutzerdefinierten Protokolls wird der MMA mit den Ablageorten der Logdateien konfiguriert. Danach registriert er automatisch Änderungen in den Logdateien und überträgt diese an den Log-Analytics-Arbeitsbereich direkt in das benutzerdefinierte Protokoll [6]. Nebenbei erwähnt kann der MMA auch außerhalb von Azure verwendet werden, beispielsweise auf einem On-Site-Server (Abb. 2). Das ist vor allem dann praktisch, wenn bei der Migration indieCloud nicht alle Anwendungen auf einen Streich migriert werden.

 

Abb. 2: Logdaten mit dem Log Analytics Agent (MMA) sammeln

 

Ist der MMA samt benutzerdefiniertem Protokoll eingerichtet, sollten nach ein paar Minuten die ersten Logdaten im Log-Analytics-Arbeitsbereich sichtbar sein (Abb. 3).

 

Abb. 3: Logdaten im eigenen Protokoll inAzure abfragen

 

Die Sammlung der Logdaten mittels MMA ist zwar schnell und einfach umgesetzt, doch hat sie auch Einschränkungen. So werden zum Beispiel zirkuläre Schreibvorgänge, bei denen alte Einträge in der Logdatei mit neuen Meldungen überschrieben werden, vom MMA nicht unterstützt [6]. Auch muss die Logdatei entweder nach dem ASCII- oder dem UTF-8-Standard kodiert sein, damit der MMA den Inhalt verarbeiten kann. Ein weiterer Wermutstropfen könnte in der Anzahl der Anwendungen liegen, aus denen das Legacy-System besteht. Bei vielen Anwendungen mit unterschiedlichen Logdateien erweist sich die manuelle Konfiguration der MMAs als zeitraubende Tätigkeit.

 

Abb. 4: Aufbereitete Logdaten im eigenen Protokoll inAzure

 

Wir haben nun die Anwendungslogs erfolgreich nach Azure übertragen und möchten sie auswerten. In der aktuellen, rohen Form ist eine Auswertung etwas schwierig, da wir alle Attribute der Logmeldung (Datum, Level, Logger, Meldung) in einem String zusammengefasst haben (in unserem Beispiel wäre das das Feld RawData). Hier kommt die Kusto Query Language (kurz KQL) ins Spiel. Je nach Aufbau der Logmeldungen lassen sich die Attribute durch geschickte KQL-Anweisungen extrahieren [7]. Mit dem in Listing 2 gezeigten KQL können die Logeinträge aus unserem Beispiel in ihre Bestandteile aufgelöst werden (Abb. 4).

 

Listing 2
.MyLog_CL
| extend _firstDashIdx = indexof(RawData, '-', 20)
| extend _secondDashIdx = indexof(RawData, '-', _firstDashIdx + 1)
| extend
   Datum = substring(RawData, 0, 10),
   Zeit = substring(RawData, 11, 12),
   Level = trim_end(@"[^\w]+", substring(RawData, 24, 5)),
   Logger = substring(RawData, _firstDashIdx+1, _secondDashIdx-_firstDashIdx-2),
   Meldung = substring(RawData, _secondDashIdx + 1)
| project Datum, Zeit, Level, Logger, Meldung

 

Statt die einzelnen Attribute einer Logmeldung über KQL-Abfragen jedes Mal zu extrahieren, kann inAzure auch von benutzerdefinierten Feldern Gebrauch gemacht werden. Benutzerdefinierte Felder können als Tabellenspalten betrachtet werden und werden direkt von Azure beim Schreiben der Logmeldungen befüllt. Zur Erstellung von benutzerdefinierten Feldern markiert man im Ergebnisbereich einer KQL-Abfrage den Teil des Textes, der für das benutzerdefinierte Feld extrahiert werden soll. Eine ausführliche Anleitung findet sich in Microsofts Onlinedokumentation [8]. Aufgrund des markierten Textbereiches ermittelt eininAzureintegrierter Algorithmus automatisch, welche Textteile zum benutzerdefinierten Feld gehören und welche nicht (Abb. 5).

 

Abb. 5: Benutzerdefinierte Felder extrahieren

MyLog_CL
| project TimeGenerated, Level_CF, Logger_CF, Meldung_CF

 

Die benutzerdefinierten Felder lassen sich zwar einfacher auswerten, doch kann es vorkommen, dass der von Azure verwendete Algorithmus die Felder nicht mit den richtigen Informationen befüllt. Das ist vor allem dann der Fall, wenn sich das Format der Logmeldungen aufgrund von neuen Attributen ändert. Azure verlässt sich bei der Extraktion von benutzerdefinierten Feldern darauf, dass das Format der Logmeldungen stabil bleibt. Das heißt, würde man die bestehenden Logmeldungen um weitere Attribute ergänzen, z. B. Thread-ID, IP-Adresse, Benutzername usw., könnte das das Log durcheinanderbringen. Man könnte indiesem Fall zwar auf eine eigene Extraktionsanweisung mittels KQL (wie in Abb. 4) zurückgreifen, doch es geht auch anders.

 

Abb. 6: Benutzerdefinierte Felder abfragen

 

Variante 2: HTTP-Datensammler-API

Azure Log Analytics bietet zusätzlich ein HTTP-Datensammler-API (nachfolgend Web-API) an, an das die Logmeldungen direkt übertragen werden können. Microsoft stellt eine Beispielimplementation in seiner Onlinedokumentation bereit, mit der sich das Web-API ansprechen lässt [9]. Doch statt das Web-API selbst anzusprechen, kann man auf bequemere Mittel zurückgreifen. Eines davon wäre das Open-Source-Projekt Log4ALA, das als NuGet-Paket frei verfügbar ist [10]. Log4ALA bietet einen für den Azure-Log-Analytics-Arbeitsbereich konfigurierbaren log4net-Appender (Listing 3), der sich direkt in die eigene Anwendungskonfiguration integrieren lässt.

 

Listing 3
<log4net>
  ...
  <appender name="Log4ALAAppender" type="Log4ALA.Log4ALAAppender, Log4ALA">
    <!-- Hier die ID des Log-Analytics-Arbeitsbereichs eintragen -->
    <workspaceId value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
    <!-- Hier den primären oder sekundären Zugriffschlüssel eintragen -->
    <SharedKey value="....................................==" />
    <!-- Name des benutzerdefinierten Protokolls -->
    <logType value="MyLog4ALA_CL" />
    <!-- Optional: Soll das Log-Level mitübertragen werden? (Standard: true) -->
    <appendLogLevel value="true" />
    <!-- Optional: Soll der Logger mitübertragen werden? (Standard: true) -->
    <appendLogger value="true" />
  </appender>
  <root>
    <appender-ref ref="Log4ALAAppender" />
  </root>
</log4net> 

 

Für die Authentifizierung gegenüber dem Web-API ist neben der Log-Analytics-Arbeitsbereich-ID auch einer der beiden Zugriffsschlüssel (primär oder sekundär) benötigt. Hierbei handelt es sich um schützenswerte Informationen, die nicht in der Quellcodeverwaltung eingecheckt werden sollten.

Gesetzt den Fall, dass es bei der Übertragung der Anwendungslogs zu Verbindungsproblemen kommt, kann Log4ALA so konfiguriert werden, dass nicht übertragene Logmeldungen in einer lokalen Textdatei niedergeschrieben werden, sodass sie nicht verloren gehen. Weiter kann Log4ALA auch mehr als nur einen Log-Analytics-Arbeitsbereich zeitgleich ansprechen. Im Gegensatz zu den einfachen Codebeispielen von Microsoft zur Ansteuerung des Web-APIs bietet Log4ALA eine interne Verarbeitungs-Queue, die verhindert, dass es bei der Übermittlung der Anwendungslogs über das Netzwerk zu Ausführungsverzögerungen innerhalb der .NET-Anwendung kommt.

Anders als in der ersten Variante mit dem MMA lässt sich bei der Log4ALA-Variante kein Logmuster mit Attributen konfigurieren. Sofern nichts weiter konfiguriert ist, füllt Log4ALA lediglich die Attribute Level, Logger und Meldung eines Logeintrags aus (Abb. 7).

 

Abb. 7: Durch Log4ALA protokollierte Logmeldungen abfragen

Möchte man zusätzliche Attribute protokollieren, so bietet Log4ALA zwei Möglichkeiten. Die eine Möglichkeit besteht darin, die Anwendungslogs im JSON-Format im Code zu verfassen. Die andere setzt voraus, dass die Attribute und Werte einer Logmeldung mit einem Trennzeichen (z. B. Gleichheitszeichen =) und Separator (z. B. Semikolon 😉 getrennt sind. Log4-ALA kann so konfiguriert werden, dass es aufgrund des Trennzeichens und Separators automatisch Attribute und ihre zugehörigen Werte erkennt und für sie benutzerdefinierte Felder in Azure erstellt, die wiederum einfach abgefragt werden können (Listing 4).

 

Listing 4
public void Log4AlaSample(int requestId)
{
  ILog log = LogManager.GetLogger(this.GetType());
 
  // Logmeldungen im JSON-Format:
  // {"requestId":"123456", "message":"Anfrage eingetroffen"}
  log.Info($"{{\"requestId\":\"{requestId}\", " +
           + $"\"message\":\"Anfrage eingetroffen\"}}");
 
  // Logmeldungen mit '=' als Trennzeichen und ';' als Separator:
  // requestId=123456; message=Anfrage eingetroffen
  log.Info($"requestId={requestId};message=Anfrage eingetroffen");
}

 

Beide vorgestellten Möglichkeiten funktionieren nur, sofern die eigene Legacy-Anwendung bereits indiesem Format die Anwendungslogs schreibt. Möchte man bestimmte Attribute nachträglich abfüllen, müssen Änderungen im Code vorgenommen werden, was bei Legacy-Anwendungen selten eine willkommene Lösung ist.

 

Variante 3: Application Insights

Falls man neben dem reinen Anwendungslog noch zusätzliche Telemetriemetriken wie Arbeitsspeicher, CPU, Netzwerklatenz usw. sammeln und auswerten möchte, könnte auch der Einsatz des Diensts Application Insights in Frage kommen. Application Insights ist die Rundum-Monitoring-Lösung für Anwendungen in der Azure-Cloud [11].

Zur Anbindung von Application Insights in unsere Beispielanwendungen stellt Microsoft ein NuGet-Paket mit einem konfigurierbaren log4net-Appender zur Verfügung [12]. Anschließend kann der Appender in der log4net-Konfiguration ergänzt werden (Listing 5).

 

Listing 5
<log4net>
  ...
  <appender name="aiAppender" type="Microsoft.ApplicationInsights.Log4NetAppender.ApplicationInsightsAppender, Microsoft.ApplicationInsights.Log4NetAppender">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%message%newline" />
    </layout>
  </appender>
  <root>
    <appender-ref ref="aiAppender" />
  </root>
</log4net>

 

Damit der Application-Insights-Appender die Daten auch überträgt, muss die Telemetriekonfiguration eingerichtet werden. Sie benötigt, anders als bisher, nicht die Log-Analytics-Arbeitsbereich-ID, sondern den Instrumentierungsschlüssel des Application-Insights-Diensts, der mit dem Log-Analytics-Arbeitsbereich inAzure verknüpft ist. Der Instrumentierungsschlüssel kann entweder über eine ApplicationInsights.config-Datei oder direkt im Quellcode mit der nachfolgenden Anweisung konfiguriert werden [13]:

 

TelemetryConfiguration.Active.InstrumentationKey =
  "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";

 

Wenn alles richtig konfiguriert ist, erscheinen die Logmeldungen im Log-Analytics-Arbeitsbereich in dem für Application Insights vorgegebenen Protokoll AppTraces (Abb. 8).

 

Abb. 8: Durch Application Insights protokollierte Logmeldungen abfragen

 

Dass der Application-Insights-Dienst derart viele Daten sammelt, hat nicht nur Vorteile. Durch die vielen Mehrdaten, die protokolliert werden, fallen auch höhere Kosten an. Daher lohnt es sich hier, die Konfiguration des Diensts nochmals zu prüfen und nicht benötigte Telemetriedaten vom Monitoring auszuschließen.

 

Wie weiter?

Dank der Tatsache, dass wir die Anwendungslogs im Log-Analytics-Arbeitsbereich konzentriert haben, erschließen sich uns nun neue Möglichkeiten [14]. Wir können beispielsweise in regelmäßigen Abständen unsere Anwendungslogs prüfen und proaktiv Warnungen über E-Mail oder SMS versenden, sobald Ungereimtheiten in der Anwendungsausführung ersichtlich sein sollten. Außerdem sind wir nun in der Lage, basierend auf unseren KQL-Abfragen ausgeklügelte Reports mittels PowerBI zu erstellen, grafische Diagramme direkt im Azure-Dashboard einzubinden oder durch den Azure-Sentinel-Dienst [15] vorzeitig auf mögliche Sicherheitsbedrohungen zu reagieren. Darüber hinaus können die Daten auch über ein Web-API für sonstige Weiterverarbeitungen bereitgestellt werden. Da das Azure-Dienstangebot ständig ausgeweitet wird, kommen fortlaufend neue Verarbeitungsmöglichkeiten hinzu.

 

Zusammenfassung

Wir haben nun drei Möglichkeiten besprochen, wie die Anwendungslogs in der Azure-Cloud gesammelt und ausgewertet werden können, sowie weitere Optionen angesprochen, wie diese Daten weiterverarbeitet werden könnten. Tabelle 1 gibt eine grobe Zusammenfassung. Wie immer gilt es, die Vor- und Nachteile der vorgestellten Lösungsansätze abzuwägen und diejenige Variante zu identifizieren, diedie eigene Problemstellung am besten beantwortet.

Variante Vorteile Nachteile
1. Log Analytics Agent + benutzerdefinierte Felder können bei klarem Logformat einfach definiert werden

+ ohne Änderungen am bestehenden Code einsetzbar, da nur die Logdateien berücksichtigt werden

– nachträgliche Anpassungen am Logformat können sich negativ auf benutzerdefinierte Felder auswirken

– für die Logdateien gelten Einschränkungen (kein zirkuläres Schreiben, nur ASCII- oder UTF-8-Format)

– hat man ein System mit sehr vielen Anwendungen, kann die manuelle Einbindung der Logdateien zeitraubend sein

2. HTTP-Datensammler-API + dank Open-Source-Bibliotheken wie Log4ALA ist eine Einbindung sehr einfach – ohne intelligenten Puffermechanismus (wie ihn z. B. Log4ALA mittels interner Queue besitzt) können viele Nachrichten die Ausführung der Anwendung negativ beeinflussen

– nachträgliches Ergänzen von benutzerdefinierten Felder kann umständliche Anpassungen im Quellcode erfordern

3. Application Insights + zusätzliche Telemetriedaten können sehr nützlich für Fehleranalyse sein – höhere Kosten, da viel mehr Daten gesammelt werden

Tabelle 1: Zusammenfassung der Vor- und Nachteile der vorgestellten Varianten

Links & Literatur

[1] Krummenacher, Roland: „Sammeln von Client-Log-Dateien in der Cloud“; in: Windows Developer 10.2014

[2] https://logging.apache.org/log4net/

[3] https://docs.microsoft.com/de-de/azure/azure-monitor/logs/quick-create-workspace

[4] https://www.youtube.com/watch?v=N-aYZ3WDRII

[5] https://docs.microsoft.com/de-de/azure/azure-monitor/agents/agent-windows

[6] https://docs.microsoft.com/de-de/azure/azure-monitor/agents/data-sources-custom-logs

[7] https://docs.microsoft.com/de-de/azure/azure-monitor/logs/parse-text

[8] https://docs.microsoft.com/de-de/azure/azure-monitor/logs/custom-fields

[9] https://docs.microsoft.com/de-de/azure/azure-monitor/logs/data-collector-api

[10] https://ptv-logistics.github.io/Log4ALA

[11] https://docs.microsoft.com/de-de/azure/azure-monitor/app/app-insights-overview

[12] https://www.nuget.org/packages/Microsoft.ApplicationInsights.Log4NetAppender

[13] https://docs.microsoft.com/en-us/azure/azure-monitor/app/configuration-with-applicationinsights-config

[14] https://docs.microsoft.com/de-de/azure/azure-monitor/logs/data-platform-logs

[15] https://azure.microsoft.com/de-de/services/azure-sentinel

 

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, TFS, C# & mehr

Agile & DevOps
Best Practices & mehr

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

HTML5 & JavaScript
Leichtegewichtig entwickeln

User Interface
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud & Azure
Cloud-basierte & Native Apps