Schau mir in die Augen – Anwendungsüberwachung mit Azure Application Insights

Mit Application Insights liefert Microsoft einen Dienst zur Anwendungsüberwachung für Entwicklung und DevOps. Damit kann so gut wie alles erfasst werden – von Antwortzeiten und -raten über Fehler und Ausnahmen, Seitenaufrufe, Benutzer (-Sitzungen), Backend bis hin zu Desktop-Anwendungen.

Die Überwachung beschränkt sich keinesfalls nur auf Webseiten. Application Insights lässt sich auch bei Webdiensten sowie im Backend einsetzen. Sogar Desktop-Anwendungen lassen sich überwachen. Anschließend können die Daten über unterschiedliche Wege analysiert und ausgewertet werden (siehe Abbildung 1).

Einsatzmöglichkeiten von Application Insights
Abbildung 1: Einsatzmöglichkeiten von Application Insights (Quelle: https://docs.microsoft.com/de-de/azure/azure-monitor/app/app-insights-overview)

Loggen

Als Ausgangsbasis wird eine Azure Subscription mit einer Application-Insights-Instanz benötigt. Ist diese angelegt, findet man in der Übersicht den sogenannten Instrumentation Key – dieser fungiert als Connection String.

Sobald die Instanz bereitgestellt wurde, kann auch schon mit der Implementierung begonnen werden. Programmiertechnisch ist man hier keinesfalls auf Azure-Ressourcen oder .Net beschränkt. Microsoft unterstützt eine Vielzahl an Sprachen und Plattformen.

Als Beispiel dient hier eine kleine .Net-Core-Konsolenanwendung. Dazu muss lediglich das NuGet-Paket Microsoft.ApplicationInsights eingebunden werden und schon kann es losgehen.

Als erstes wird ein Telemetry Client erstellt. Hier wird einfach der passende Instrumentation Key aus der eigenen Application-Insights-Instanz eingefügt und schon ist die Anwendung bereit für die ersten Log-Einträge.

  • Mit Trace erzeugt man einen einfachen Trace-Log-Eintrag mit entsprechender Message und passendem Severity Level.
  • Events eignen sich für strukturierte Logs, welche sowohl Text als auch numerische Werte enthalten können.
  • Metrics sind dagegen ausschließlich numerische Werte und dienen daher vor allem zur Erfassung regelmäßiger Ereignisse.
static void Main(string[] args)
{
Console.WriteLine("Schau mir in die Augen");

       var config = TelemetryConfiguration.CreateDefault();
       config.InstrumentationKey = "INSTRUMENTATIONKEY";
       var tc = new TelemetryClient(config);

       // Track traces
       tc.TrackTrace("BlogTrace", SeverityLevel.Information);

       // Track custom events
       var et = new EventTelemetry();
       et.Name = "BlogEvent";
       et.Properties.Add("Source", "console");
       et.Properties.Add("Context", "Schau mir in die Augen");
       tc.TrackEvent(et);

       // Track custom metric
       var mt = new MetricTelemetry();
       mt.Name = "BlogMetric";
       mt.Sum = new Random().Next(1,100);
       tc.TrackMetric(mt);

       tc.Flush();
}

Als Hinweis sei noch erwähnt, dass die Log-Einträge mit bis zu fünf Minuten Verzögerung im Application Insights erscheinen.

Zusammenspiel mit NLog

Application Insights lässt sich mit wenigen Schritten auch in eine bestehende NLog-Konfiguration einbinden.

Dazu müssen das NuGet-Paket Microsoft.ApplicationInsights.NLogTarget installiert und danach die NLog-Konfiguration um folgende Einträge erweitern werden:

  • Extensions mit dem Verweis auf die Application Insights Assembly hinzufügen
  • Neues Target vom Typ Application Insights Target (hier wieder den eigenen Instrumentation Key angeben)
  • Neue Regel mit Ziel auf das Application Insights Target
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      throwConfigExceptions="true">

  <extensions>
    <add assembly="Microsoft.ApplicationInsights.NLogTarget" />
  </extensions>

  <targets>
    <target name="logfile" xsi:type="File" fileName="log.txt" />
    <target name="logconsole" xsi:type="Console" />
    <target xsi:type="ApplicationInsightsTarget" name="aiTarget">
      <instrumentationKey>INSTRUMENTATIONKEY</instrumentationKey>
      <contextproperty name="threadid" layout="${threadid}" />
    </target>
  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="logconsole" />
    <logger name="*" minlevel="Debug" writeTo="logfile" />
    <logger name="*" minlevel="Trace" writeTo="aiTarget" />
  </rules>
</nlog>

Auswertung

Die Auswertung erfolgt anschließend über das Application-Insights-Portal. Sämtliche Logs finden sich anschließend unter Protokolle in der jeweiligen Tabelle (siehe Abbildung 2).

Auswertungen mit Application Insights
Abbildung 2: Auswertungen mit Application Insights

Die in der Konsolenanwendung erzeugten Trace-Logs können der Tabelle traces entnommen werden. Abfragen werden mit der Kusto Query Language (KQL) formuliert. Die Traces aus dem obigen Beispiel können über folgende Query abgefragt werden:

traces
| where message contains "BlogTrace"

Die geloggten Metriken lassen sich mit folgender Abfrage auch grafisch als Liniendiagramm darstellen (siehe Abbildung 3):

customMetrics
| where timestamp >= ago(12h)
| where name contains "Blog"
| render timechart 
Grafische Darstellung der geloggten Metriken
Abbildung 3: Grafische Darstellung der geloggten Metriken

Dashboards & Warnungsregeln

Um Unregelmäßigkeiten frühzeitig zu erkennen, können individuelle Dashboards und Warnungsregeln angelegt werden. Im Falle der oben verwendeten Metriken kann man das Diagramm an ein freigegebenes Dashboard anheften. Dies lässt sich mit weiteren Abfragen beliebig fortsetzen, bis die gewünschten Informationen zu einer Übersicht zusammengetragen sind.

Das folgende Dashboard zeigt die Metrik der Konsolenanwendung. Gleichzeitig sind darin auch exemplarisch Informationen über Serveranfragen, fehlerhafte Anfragen, Antwortzeiten sowie Leistung und Verfügbarkeit enthalten (siehe Abbildung 4).

Informationen auf einen Blick auf dem Dashboard
Abbildung 4: Informationen auf einen Blick auf dem Dashboard

Hat man das Dashboard mal nicht im Blick und es kommt zu Anomalien, kann man sich auch über Warnungsregeln direkt per E-Mail oder SMS informieren lassen.

Einzelne Warnungsregeln lassen sich über den Menüpunkt Warnungen im Application-Insights-Portal anlegen und verwalten. Eine Warnungsregel besteht aus einer Signallogik (Bedingung) sowie einer Aktionsgruppe.

Bei der Bedingung wird ein Signal, z. B. eine Metrik, ausgewählt und mit einem Schwellwert versehen: „traces größer als 80“. Erhält man nun innerhalb eines definierten Zeitraumes mehr als 80 trace-Einträge, wird die Warnung ausgelöst.

Die Aktionsgruppe legt abschließend fest, was im Falle einer Warnung zu tun ist. Hier lassen sich einfache Hinweis-E-Mails oder SMS an festgelegte Personen verschicken oder komplexere Handlungen programmatisch über Runbooks, Azure Functions, Logik-Apps oder Webhooks abbilden (siehe Abbildung 5).

Verschiedene Aktionstypen in einer Aktionsgruppe
Abbildung 5: Verschiedene Aktionstypen in einer Aktionsgruppe

REST API

Besteht der Bedarf, die Daten auch außerhalb von Application Insights zu verarbeiten, können diese über eine REST API abgefragt werden.

Die URL für die API-Aufrufe setzt sich aus einem Basis-Teil und der gewünschten Operation zusammen. Operationen sind metrics, events oder query. Dazu muss noch ein API-Key als „X-API-Key“ HTTP-Header übergeben werden:

https://api.applicationinsights.io/v1/apps/{app-id}/{operation}/[path]?[parameters]

Die App-ID ist in den Settings unter API Access zu finden.

Abbildung 6: URL der API-Aufrufe
Abbildung 6: URL der API-Aufrufe (Quelle: https://dev.applicationinsights.io/quickstart)

Um bei den oben beschriebenen Metriken zu bleiben, hier der API-Aufruf mit query-Operation für die Anzahl aller Einträge der letzten 24 Stunden:

https://api.applicationinsights.io/v1/apps/{}app-id}/query?query=customMetrics | where timestamp >= ago(24h) | where name contains „Blog“ | summarize count()

Das Ergebnis kommt im JSON-Format zurück:

{
    "tables": [
        {
            "name": "PrimaryResult",
            "columns": [
                {
                    "name": "count_",
                    "type": "long"
                }
            ],
            "rows": [
                [
                    13
                ]
            ]
        }
    ]
}

Fazit

Wie anhand dieses kleinen Beispiels zu sehen ist, lässt sich ein zentralisiertes Logging mit Hilfe von Application Insights in wenigen Schritten aufbauen und verwalten. Neben der schnellen und einfachen Integration ist auch die automatisierte Infrastruktur von Vorteil. Man muss sich um kein Hosting kümmern und sollte die Last einmal steigen, skaliert Application Insights automatisch.

Mocks in der Testumgebung (Teil 1)

Die Komplexität und Interaktion der Softwareanwendungen, die in Unternehmen eingesetzt wird, ist in den letzten Jahren stark gestiegen. Werden heute Releases ausgerollt, steht ein Teil der neuen Funktionen immer im Zusammenhang mit dem Datenaustausch zu anderen Anwendungen. Das merken wir auch im Bereich Softwaretest: Der Fokus der Tests hat sich von reinem Systemtest auf den Bereich der Integrationstests erweitert. Darum kommen auch Mocks zum Einsatz.

Wir arbeiten in einem Testcenter und führen für unsere Kunden den übergreifenden Integrationstest über seine komplexe Anwendungsinfrastruktur durch. Daher bauen wir die Testumgebungen passend zur Produktivumgebung auf. Also wozu brauchen wir dann Mocks, wenn die Integrationstestumgebung bereits alle Software-Komponenten besitzen?

Zum Teil liegt man mit dieser Denkweise schon richtig, allerdings werden auf den unterschiedlichen Staging-Ebenen beim Test verschiedene Fokusse gesetzt. Mocks werden im Systemtest eher weniger verwendet, da in diesem Bereich die Funktionen der Software getestet werden. Dafür werden Mocks häufiger in Integrationstest verwendet. Da es aber nicht immer möglich ist, den kompletten Nachrichtenfluss zu testen, wird als Alternative die Kommunikation an der Schnittstelle geprüft.

Werden im Integrationstest 3 Komponenten getestet, die im Nachrichtenfluss direkt hintereinanderliegen, kann bei Verwendung aller 3 Softwarekomponenten nicht hundertprozentig sichergestellt werden, dass die Prozedur ohne Probleme läuft. Es könnte sein, dass die jeweiligen Fehler der Komponenten den Nachrichtenfluss verfälschen und im Nachhinein wieder richtiggestellt wird, der Fehler quasi maskiert wird. Daher stellen wir Mocks an die jeweiligen Enden der Komponenten, um gezielte Ein- und Ausgaben zu bekommen.

Um die beschriebenen Probleme und Eigenheiten eines Mocks zu erklären, nutzen wir als zu testende Anwendung einen Rest-Services. Der Rest-Service sollte in der Lage sein, ein GET und ein POST–Befehl durchzuführen. Würde unsere Komponente nun nach personenspezifischen Daten beim Mock nachfragen mit einem GET-Befehl, könnten wir unseren Mock so konfigurieren, dass er standardisierte Antworten zurück gibt oder bei POST-Befehlen einfache Berechnungen durchführt.

Mit Microsoft Visual Studio kam man in C# schnell ein WebAPI-Projekt erstellen, welches die grundlegende Funktion eines Rest-Services besitzt. Die Methoden des Controllers müssen nur noch angepasst werden und man hat eine funktionierende Rest-API zur Verfügung.

Datei > Neu > Projekt > Visual C# > Web > Webanwendung

Wenn man sich die Controller der WebAPI anschaut, kann man sehen, dass bestimmte URLs, Funktionen innerhalb der API aufrufen. In diesem Beispiel wird eine kleine WebAPI verwendet, die als Helden-Datenbank genutzt wird.

Heldenliste

Die Route beschreibt den URL-Pfad um die HttpGet(GET-Befehl)- oder HttpPost(POST-Befehl) Funktion aufzurufen.

Beispiel: http:localhost/api/helden/add

Sobald man die Rest-API zum Laufen gebracht hat, ist es mit einem API-Tool(Postman) oder einem Browser möglich, unterschiedliche Rest-Befehle an die URL zu schicken. Wenn nun ein POST-Befehl an die Rest-API geschickt wird, nimmt der Service diesen an und erkennt anhand der URL, dass die Funktion AddHeldenDetails() aufgerufen werden soll. Die Funktion nimmt die mitgeschickten Daten auf und fügt sie ihrer Datenbank hinzu. Als Antwort liefert sie den Return-Wert der Funktion. In diesem Fall die Bestätigung über das Hinzufügen des gewünschten Heldes.

POST-Befehl:

POST /api/Helden/zwei HTTP/1.1
Host: localhost:53521
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: b169b96f-682d-165d-640f-dbcafe52789e
{ "Name":"Maria","Klasse": "Lord","Alter":68,"Level":57 }

Antwort:

Der Held mit dem Namen: Maria wurde hinzugefügt

Wir haben nun mit unserem POST-Befehl die Heldin Maria der Datenbank hinzugefügt. Nun können mit dem GET-Befehl die gespeicherten Helden abrufen werden. Hier ein Beispiel zum Format des GET-Befehls, welcher an die Rest-API geschickt wird mit der dazugehörigen Antwort der API:

Abfrage:

GET /api/helden/get HTTP/1.1
Host: localhost:53521
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: b3f19b01-11cf-85f1-100f-2cf175a990d9

Antwort:

Antwort der API

In der Antwort ist zusehen, dass die Heldin Maria der Liste hinzugefügt wurde und nun jederzeit abgerufen werden kann.

Nun wissen wir um die Funktionsweise den Rest-Services Bescheid, welcher Input zu welchem Output führt. Mit dieser Information können wir uns dann an den Bau eines Mocks begeben. Mit diesem Thema werde ich mich im nächsten Teil genauer befassen.

Das war „Mocks in der Testumgebung Teil 1“ … Teil 2 folgt 🙂