# Performance & Laufzeiten

> KQL-Abfragen für Laufzeiten, Fehlerquoten, Abhängigkeiten und Datenvolumen.

---

Site [llms.txt](/llms.txt)

---

Die folgenden Abfragen können direkt im **Logs**-Bereich von Application Insights
(Kusto Query Language) ausgeführt werden.

---

## Laufzeiten und Fehler

### Laufzeiten aller Funktionen (letzte 24 Stunden)

```kusto
requests
| where timestamp > ago(24h)
| summarize
    Aufrufe        = count(),
    DurchschnittMs = avg(duration),
    P95Ms          = percentile(duration, 95),
    Fehler         = countif(success == false)
  by name
| order by DurchschnittMs desc
```

### Verlauf einer einzelnen Funktion (letzte 7 Tage)

```kusto
requests
| where timestamp > ago(7d)
| where name == "GetChanges"
| summarize
    P50Ms  = percentile(duration, 50),
    P95Ms  = percentile(duration, 95),
    Fehler = countif(success == false),
    Läufe  = count()
  by bin(timestamp, 1h)
| order by timestamp asc
```

### Durchschnittliche Laufzeit der letzten 30 Tage (täglich)

```kusto
requests
| where timestamp > ago(30d)
| summarize
    DurchschnittMs = avg(duration),
    P95Ms          = percentile(duration, 95)
  by name, bin(timestamp, 1d)
| order by name asc, timestamp asc
```

### Fehlerquote je Funktion (letzte 7 Tage)

```kusto
requests
| where timestamp > ago(7d)
| summarize
    Gesamt    = count(),
    Fehler    = countif(success == false),
    FehlerPct = round(100.0 * countif(success == false) / count(), 1)
  by name
| order by FehlerPct desc
```

### Warnungen und Fehler im Zeitverlauf

```kusto
traces
| where timestamp > ago(7d)
| where severityLevel >= 2  // 2 = Warning, 3 = Error
| summarize Anzahl = count() by severityLevel, bin(timestamp, 1h), operation_Name
| order by timestamp asc
```

### Top-10 häufigste Fehlermeldungen

```kusto
exceptions
| where timestamp > ago(7d)
| summarize Anzahl = count() by outerMessage
| top 10 by Anzahl desc
```

---

## Einzelne Ausführung nachvollziehen

### Alle Einträge einer Ausführung (per operation_Id)

```kusto
union requests, traces, exceptions
| where operation_Id == "<operation_Id hier einfügen>"
| project timestamp, itemType, severityLevel, message, outerMessage, operation_Name
| order by timestamp asc
```

### Fehlerläufe mit allen zugehörigen Traces (letzte 24 Stunden)

```kusto
let fehlgeschlagene =
    requests
    | where timestamp > ago(24h)
    | where success == false
    | project operation_Id, FunktionsName = name, Startzeit = timestamp;
traces
| where timestamp > ago(24h)
| join kind=inner fehlgeschlagene on operation_Id
| project Startzeit, FunktionsName, severityLevel, message
| order by Startzeit desc, severityLevel desc
```

---

## Abhängigkeiten

### Langsamste Abhängigkeiten (letzte 24 Stunden)

Zeigt die ausgehenden Aufrufe an Quell- und Zielsysteme mit den höchsten Laufzeiten:

```kusto
dependencies
| where timestamp > ago(24h)
| summarize
    Aufrufe        = count(),
    DurchschnittMs = avg(duration),
    P95Ms          = percentile(duration, 95),
    Fehler         = countif(success == false)
  by type, target, name
| order by P95Ms desc
| take 20
```

> **Hinweis**
>
> Erfolgreiche, schnelle Aufrufe werden gesampelt (siehe
> [Sampling von Abhängigkeiten](../#sampling-von-abhängigkeiten)). Die Spalte `Aufrufe` ist
> deshalb **keine vollständige Zählung**. Langsame und fehlgeschlagene Aufrufe sind dagegen
> vollständig enthalten — für genau diese Auswertung ist die Abfrage also aussagekräftig.



### Fehlgeschlagene Abhängigkeiten nach Zielsystem (letzte 7 Tage)

```kusto
dependencies
| where timestamp > ago(7d)
| where success == false
| summarize Fehler = count() by type, target, resultCode
| order by Fehler desc
```

---

## Datenvolumen und Kosten

### Datenvolumen pro Tabelle

Wenn das tägliche Datenvolumen die konfigurierte Obergrenze (Daily Cap) erreicht, zeigt
diese Abfrage, welche Tabelle den Verbrauch verursacht:

```kusto
union withsource = Tabelle *
| where TimeGenerated > ago(7d)
| summarize VolumenMB = round(sum(_BilledSize) / 1024.0 / 1024.0, 1) by Tabelle, bin(TimeGenerated, 1d)
| order by TimeGenerated desc, VolumenMB desc
```

> **Hinweis**
>
> Erfahrungsgemäß dominiert `AppDependencies` das Volumen deutlich. Greift die Obergrenze
> trotz aktivem Sampling, ist `TELEMETRY_DEPENDENCY_SAMPLE_RATE` weiter zu reduzieren.
> Zusätzlich ist zu prüfen, ob `TELEMETRY_CONTROL` versehentlich auf `TRACE_ALL` steht —
> damit ist jedes Sampling deaktiviert.



---

## Hinweise zur Interpretation

### Warum fehlen manche Traces bei erfolgreichen Läufen?

Das Buffering sendet bei erfolgreichen Läufen nur einen Teil der `Information`-Einträge
und keine `Debug`-Einträge. Die Stichprobe wird **pro Eintrag** gezogen: Jede einzelne
`Information`-Zeile hat unabhängig eine Chance von 20 % (konfigurierbar via
`TELEMETRY_INFO_SAMPLE_RATE`), gesendet zu werden. Das führt zu einem gleichmäßigen Strom
vereinzelter Traces — vollständige, lückenlose Ketten gibt es nur bei Warning- und
Fehler-Läufen.

`Trace`-Einträge erreichen Application Insights grundsätzlich nie — sie sind nur live in
der Konsole bzw. im Log Stream sichtbar.

### Warum fehlen manche Abhängigkeiten?

Fehlgeschlagene und langsame Aufrufe kommen immer vollständig an, erfolgreiche und
schnelle nur zu einem Anteil von 10 % (konfigurierbar via
`TELEMETRY_DEPENDENCY_SAMPLE_RATE`).

Anders als bei den Log-Einträgen wird diese Entscheidung **pro Aufruf** getroffen — in dem
Moment, in dem der Aufruf endet, also bevor das Ergebnis der Ausführung bekannt ist.
In der End-to-End-Transaktionsansicht sind daher auch bei einem **fehlgeschlagenen** Lauf
nicht zwangsläufig alle ausgehenden Aufrufe zu sehen. Für eine lückenlose Kette ist
temporär `TELEMETRY_CONTROL=TRACE_ALL` zu setzen.

### Requests vs. Traces vs. Dependencies

| Telemetrie-Typ | Wird immer erfasst? | Enthält Laufzeit? |
|---|---|---|
| **Request** | Ja — jede Invocation | Ja |
| **Exception** | Ja — jede Exception | Nein |
| **Trace** | Nein — abhängig vom Outcome | Nein |
| **Dependency** | Nein — garantiert nur, wenn der **Aufruf selbst** fehlschlägt oder den Langsam-Schwellwert überschreitet | Ja |
| **Custom Metric** | Ja — vorab aggregiert, kein Sampling | Nein |

Für Laufzeit-Analysen der Funktionen immer die `requests`-Tabelle verwenden, für Laufzeiten
einzelner Fremdsystem-Aufrufe die `dependencies`-Tabelle, für inhaltliche Diagnose die
`traces`-Tabelle. Für belastbare **Mengenangaben** ausschließlich die
[Custom Metrics](../metriken/), da diese nicht gesampelt werden.

### Korrelation über operation_Id

Jede Funktionsausführung erhält eine eindeutige `operation_Id`. Alle Traces, Requests und
Exceptions dieser Invocation tragen dieselbe ID. Das erlaubt es, in KQL alle
zusammengehörenden Einträge mit einem einzigen Filter zu finden.
