Für Breakpoints und Step-Through läuft die DataBridge nicht im Container, sondern lokal als
Prozess in der IDE. Zwei grundsätzlich verschiedene Varianten, je nachdem wogegen die lokal
laufende DataBridge sprechen soll:
Gegen Podman debuggen
Azurite, Podman-Datenbanken, Service-Bus-Emulator, WireMock — alles lokal, keine Azure-Kosten,
jederzeit zurücksetzbar.
Beide Varianten teilen sich denselben Code und dieselbe launchSettings.json (Rider/Visual
Studio/VS Code starten die DataBridge über dasselbe Projekt src/DataBridge/DataBridge.csproj auf
Port 7062). Der Unterschied steckt ausschließlich in src/DataBridge/local.settings.json — welche
Connection-Strings dort stehen, entscheidet, wogegen die DataBridge beim Start läuft. Die Datei ist
nicht eingecheckt (.gitignore) und wird in beiden Varianten unterschiedlich befüllt:
Podman: generiert durch dev/up.sh --host aus der Vorlage dev/local.settings.dev.json.
Cloud: von Hand aus src/DataBridge/sample.local.settings.json kopiert und mit echten
Werten gefüllt.
Beide Varianten steuern außerdem, ob GetChanges (Timer) und die Process*QueueItems-Funktionen
laufen, über dieselben App-Settings (AzureWebJobs.<FunctionName>.Disabled) — nur der Weg dorthin
unterscheidet sich (Toggle beim Start vs. Wert von Hand in local.settings.json). Details dazu in
den beiden Artikeln.
1 - Gegen Podman debuggen
Die DataBridge lokal mit Breakpoints gegen die Podman-Testumgebung debuggen — konkret in Rider, Visual Studio und VS Code, inklusive Steuerung von GetChanges/Process*QueueItems und Einblick in den Service-Bus-Emulator.
Voraussetzung ist die Podman-Testumgebung. Der databridge-Container
würde sonst Port 7062 belegen, deshalb startet up.sh/up.ps1 alles außer der DataBridge selbst:
dev\up.ps1 -HostMode mssql process
dev/up.sh --host mssql process
--host (bzw. -HostMode) lässt sich mit allen anderen Argumenten kombinieren (Datenbanken,
Mocks, getchanges, process, servicebus) und startet alles außer dem databridge-Container
selbst (dafür gibt es das Profil databridge, das sonst automatisch aktiv ist). Der Befehl
generiert zusätzlich
src/DataBridge/local.settings.json aus der Vorlage dev/local.settings.dev.json (Azurite über
UseDevelopmentStorage=true, Service-Bus-Emulator über localhost statt über den
Compose-Servicenamen servicebus, sowie die Toggles aus dev/.env, siehe
GetChanges/Process*QueueItems steuern).
Überschreibt local.settings.json bei jedem Aufruf
src/DataBridge/local.settings.json wird bei jedem--host/-HostMode-Aufruf neu erzeugt und
dabei kommentarlos überschrieben — auch echte Zugangsdaten, die dort für
Cloud-Debugging eingetragen waren, sind danach weg. Wer beides abwechselnd nutzt,
siehe Podman- und Cloud-Settings parallel halten.
Damit die Connections in Azurite ebenfalls auf den Host statt auf die Compose-Servicenamen
zeigen, setzt --host/-HostMode beim Seed-Container DATABRIDGE_RUNTIME=host (sonst
container); seed.py ersetzt dann mssql/postgres/mysql/oracle/wiremock durch
localhost mit dem passenden Host-Port aus dev/.env.
Zum Zurückwechseln auf den Container-Betrieb reicht ein Aufruf ohne --host/-HostMode — das
generiert local.settings.json nicht neu, startet aber wieder den databridge-Container auf
demselben Port. Die generierte Datei vorher löschen oder ignorieren, sie wird nicht mehr gelesen,
solange kein lokaler Prozess läuft.
In der IDE starten
Alle drei IDEs nutzen dasselbe Projekt src/DataBridge/DataBridge.csproj und dasselbe Profil
DataBridge aus src/DataBridge/Properties/launchSettings.json (startet mit --port 7062, passend
zum MCP-Endpoint und zu curl-Aufrufen gegen localhost:7062).
Das richtige Run-Profil auswählen
Rider (und die anderen IDEs) legen für jede Testklasse bzw. jeden Testfall automatisch eine eigene
Run-/Debug-Konfiguration an, sobald man einmal über die Gutter-Icons einen Test gestartet hat —
z. B. „OpenApiConnectorAuthTests“. Diese Konfigurationen bleiben oben im Auswahlmenü stehen und
werden schnell mit dem eigentlichen Funktions-Profil DataBridge verwechselt, gerade wenn Rider
zuletzt einen Test als aktive Konfiguration gemerkt hat. Startet man versehentlich eine
Test-Konfiguration statt DataBridge, laufen die Unit-Tests statt der Function App —
sichtbar u. a. daran, dass Breakpoints in Connectoren/Services nicht mehr halten oder plötzlich
Exceptions aus DataBridge.Test.* im Log auftauchen, die eigentlich von einem Test erwartet
werden (z. B. ServiceClient_WithoutCredentials_ThrowsNotSupported). Vor dem Debuggen also immer
kurz prüfen, dass oben rechts wirklich DataBridge (Projekt-Icon, nicht Test-Icon) ausgewählt
ist.
Rider
Lösung src/DataBridge.slnx öffnen (falls noch nicht offen).
Oben rechts das Run-Profil DataBridge auswählen (Rider erkennt das Azure-Functions-Projekt
automatisch und bietet das Profil aus launchSettings.json an).
Auf das Käfer-Symbol (Debug) statt Play klicken, oder Shift+F9.
Breakpoints wie gewohnt im Editor setzen (z. B. in ProcessQueueMessageService oder einem
Connector).
Visual Studio
src/DataBridge.slnx öffnen, DataBridge als Startprojekt setzen (Rechtsklick im
Projektmappen-Explorer → Als Startprojekt festlegen, falls nicht schon aktiv).
Im Dropdown neben dem grünen Play-Pfeil das Profil DataBridge wählen.
F5 (Debuggen starten) statt Ctrl+F5.
Breakpoints wie gewohnt setzen.
VS Code
VS Code braucht die Azure Functions Core Tools (func) und die Erweiterung Azure Functions
(oder C# Dev Kit), um Attach/Debug über launchSettings.json anzubieten. Ohne Erweiterung reicht
auch der direkte Weg:
Terminal in src/DataBridge öffnen.
func start (nutzt automatisch local.settings.json und den Port aus host.json/der
Umgebung — bei Bedarf func start --port 7062 explizit angeben).
Mit der Erweiterung C# Dev Kit: Debug: Attach to .NET Process ausführen und den laufenden
func-/dotnet-Prozess auswählen. Mit der Azure Functions-Erweiterung genügt stattdessen
F5 direkt im Projektordner, das startet func start automatisch im Debug-Modus.
Breakpoints wie gewohnt setzen.
Danach in jeder IDE:
Der MCP-Server databridge-dev aus .mcp.json funktioniert unverändert
(http://localhost:7062/api/mcp?code=local) — func start prüft den Function Key nicht, siehe
MCP für die Testumgebung.
Eine Anfrage auslösen (z. B. POST http://localhost:7062/api/ExecuteJob?code=local, per Bruno,
curl oder MCP run_job) und der Breakpoint hält an.
Logging
dev/local.settings.dev.json setzt kein APPLICATIONINSIGHTS_CONNECTION_STRING — es gibt also
auch im Host-Modus keine Application Insights, an die etwas gesendet werden könnte. Alles, was die
DataBridge loggt, geht direkt in die Konsole des lokalen Prozesses:
Rider: das Run-Fenster (bzw. Debug-Fenster mit Breakpoints) am unteren Rand.
Visual Studio: das Ausgabefenster (Ansicht → Ausgabe, Quelle „Debug“) sowie bei
F5 das Konsolenfenster der Function-Host-Runtime.
VS Code: das Terminal, in dem func start läuft, bzw. der Debug Console-Tab bei Attach.
src/DataBridge/host.json setzt für die Kategorien DataBridge und Function den Log-Level
Trace — dieselbe ausführlichste Stufe wie im Container (siehe
Lokale Testumgebung → Logging). Weil hier keine
Application Insights mitläuft, spielen die dortigen Sampling-/Puffer-Regeln
(TELEMETRY_INFO_SAMPLE_RATE, TELEMETRY_CONTROL) keine Rolle — sie wirken nur auf den Versand
an Application Insights, nicht auf die Konsolenausgabe.
GetChanges/Process*QueueItems steuern
Zwei unabhängige Schalter entscheiden, ob im Host-Modus etwas automatisch verarbeitet wird:
Schalter
Argument bei up.sh --host/up.ps1 -HostMode
Wirkung
DATABRIDGE_GETCHANGES
getchanges
AzureWebJobs.GetChanges.Disabled in der generierten local.settings.json
dev\up.ps1 -HostMode mssql process# Verarbeitung an, Timer ausdev\up.ps1 -HostMode mssql getchanges process# beides andev\up.ps1 -HostMode mssql # beides aus – Verarbeitung nur manuell per ExecuteJob
dev/up.sh --host mssql process # Verarbeitung an, Timer ausdev/up.sh --host mssql getchanges process # beides andev/up.sh --host mssql # beides aus – Verarbeitung nur manuell per ExecuteJob
Beide Toggles aktivieren automatisch das Profil servicebus (siehe
Service Bus), weil ohne Emulator weder GetChanges noch
Process*QueueItems sinnvoll etwas zu tun hätten. Die Durable-Functions-Kette
(StartGetChangesOrchestration, GetChangesOrchestrator, GetChangesActivity, JobState) bleibt
immer deaktiviert.
Weil local.settings.json bei jedem --host-Aufruf neu generiert wird, reicht ein erneutes
up.sh --host … mit anderen Toggles, um umzuschalten — die IDE muss dafür neu gestartet werden
(Azure-Functions-Runtime liest local.settings.json nur beim Start).
Typischer Ablauf, um einen Job Ende-zu-Ende mit Breakpoints zu verfolgen:
dev/up.sh --host mssql process (Windows: dev\up.ps1 -HostMode mssql process) — Datenbank,
Verarbeitung und Service-Bus-Emulator, aber ohne Container-DataBridge.
In der IDE debuggen starten (s. o.), Breakpoint z. B. in ProcessQueueMessageService setzen.
getchanges zusätzlich nur nötig, wenn der Timer selbst (statt ExecuteJob) die Änderungen
erkennen soll — dann muss der Job vorher per update_job auf IsActive: true gesetzt werden.
Service Bus einsehen
Drei Wege, in den Service-Bus-Emulator hineinzuschauen, ohne selbst Code zu schreiben:
Web-Oberfläche servicebus-explorer (empfohlen, auch zum aktiven Testen). Läuft automatisch
mit, sobald das Profil servicebus aktiv ist (also auch über getchanges/process) — kein
separater Start nötig. Zeigt Queues, Nachrichtenzahlen (aktiv/dead-lettered) und einzelne
Nachrichten inkl. Body in einer grafischen Oberfläche:
http://localhost:8090 (Port konfigurierbar über SERVICEBUS_EXPLORER_PORT in dev/.env)
Es handelt sich um das Community-Tool
Service-Bus-Emulator-Explorer von
Tom Biddulph (nicht von Microsoft, kein Bestandteil des offiziellen Emulators) — als eigener
Container servicebus-explorer im Compose-Stack mitgeliefert, per fertigem Image
(ghcr.io/tombiddulph/service-bus-emulator-explorer), spricht intern mit servicebus über dieselben
Connection-Strings wie die DataBridge selbst.
Anders als die MCP-Diagnose-Tools kann die Web-Oberfläche auch selbst Nachrichten auf eine Queue
oder ein Topic legen (eigener Body, Content-Type, Custom Properties, Session-ID) — praktisch, um
Process*QueueItems gezielt mit einer handgebauten Nachricht zu testen, ohne vorher GetChanges
über einen echten Job laufen zu lassen.
MCP (kein zusätzliches Werkzeug nötig, gut für Automatisierung/Scripting). Der MCP-Server
databridge-dev bietet schreibgeschützte Diagnose-Tools: list_queue_statuses/get_queue_status
(Nachrichtenzahlen je Queue) und peek_* für die Metadaten wartender Nachrichten (u. a.
Body-Größe, keine Inhalte — siehe MCP für die Testumgebung). Das läuft
über den Admin-Client auf Port 5300, den ServiceBusService beim Emulator automatisch anhängt
(siehe Service Bus).
Rohes REST/AMQP, z. B. per curl. Die Management-API des Emulators liegt auf
http://localhost:5300; für eigene Skripte, die weder die Weboberfläche noch MCP nutzen sollen,
reicht derselbe Connection-String wie oben.
Klassischer Service Bus Explorer funktioniert nicht
Das weit verbreitete Desktop-Tool Service Bus Explorer (Community-Tool von Paolo Salvatori)
unterstützt den Emulator nicht — er ist kein vollwertiger Service-Bus-Namespace und hat andere
Auth-Mechanismen. Der oben verlinkte servicebus-explorer ist eigens für den Emulator gebaut.
Für den Alltag reicht MCP
Für die üblichen Fragen beim Debuggen — „ist die Nachricht überhaupt auf der Queue angekommen?“,
„wie viele Nachrichten liegen in sbqignored?“ — reichen die MCP-Diagnose-Tools. Die
Web-Oberfläche lohnt sich, sobald man Nachrichten manuell anschauen, selbst verschicken oder
erneut zustellen will — die MCP-Tools sind bewusst rein lesend (siehe
MCP-Server) und können das nicht.
2 - Gegen echtes Azure debuggen
Die DataBridge lokal mit Breakpoints gegen eine echte Azure-Instanz debuggen — konkret in Rider, Visual Studio und VS Code, inklusive Steuerung von GetChanges/Process*QueueItems.
Hier läuft kein Podman-Stack mit — die lokal gestartete DataBridge verbindet sich direkt mit einem
echten Storage Account, einem echten Service Bus Namespace und den echten Quell-/Zielsystemen (BC,
CE, NAV, SQL). Das ist der klassische Weg, um ein Problem aus einer konkreten Umgebung
(z. B. dsyr-wysatest) lokal nachzuvollziehen oder mit dem Debugger zu untersuchen.
local.settings.json vorbereiten
src/DataBridge/sample.local.settings.json nach src/DataBridge/local.settings.json kopieren
(falls noch nicht vorhanden — die Datei ist in .gitignore, geht also nicht versehentlich in
einen Commit).
Die Platzhalter durch echte Werte aus dem Azure Portal bzw. Key Vault ersetzen:
AzureWebJobsStorage (Storage-Account-Connection-String), ExportQueueServiceBus
(Service-Bus-Connection-String) sowie die Connection-Strings/Secrets der Quell- und
Zielsysteme, je nachdem was getestet werden soll. Diese Werte niemals committen und nicht in
Chats/Tickets einfügen.
Wenn enableManagedIdentity in der Zielumgebung aktiv ist (siehe
Identity-Verbindungen bzw. SettingsService), fehlen dort
Connection-Strings ganz — stattdessen AzureWebJobsStorage__accountName und
ExportQueueServiceBus__fullyQualifiedNamespace setzen und lokal per az login angemeldet
sein, damit DefaultAzureCredential greift. Das erfordert eine Rollenzuweisung für den
eigenen Benutzer auf Storage und Service Bus (Data-Rollen, nicht nur Management), sonst schlägt
der Verbindungstest beim Start fehl.
Podman überschreibt dieselbe Datei
src/DataBridge/local.settings.json ist die einzige Datei, aus der sowohl der Podman-Host-Modus
als auch dieses Cloud-Setup lesen — es gibt keine zweite, von der Function-Runtime automatisch
erkannte Datei. Läuft danach dev/up.sh --host/dev\up.ps1 -HostMode (siehe
Debuggen → Gegen Podman), wird die Datei kommentarlos überschrieben und die
gerade eingetragenen echten Zugangsdaten sind weg. Wer beides abwechselnd nutzt, siehe
Podman- und Cloud-Settings parallel halten weiter
unten.
In der IDE starten
Identisch zum Podman-Host-Modus — alle drei IDEs nutzen dasselbe Projekt
src/DataBridge/DataBridge.csproj und dasselbe Profil DataBridge aus
src/DataBridge/Properties/launchSettings.json (startet mit --port 7062).
Das richtige Run-Profil auswählen
Leicht zu verwechseln mit einer automatisch angelegten Test-Konfiguration (Rider legt für jede
Testklasse/jeden Testfall eine eigene an, sobald man sie einmal gestartet hat). Läuft aus Versehen
ein Test statt der Function App, wirken Breakpoints in Connectoren/Services wie „tot“, und im Log
tauchen Exceptions aus DataBridge.Test.* auf, die eigentlich von einem Test erwartet werden.
Details und Beispiel siehe Debuggen → Gegen Podman. Vor dem
Debuggen prüfen, dass oben rechts wirklich DataBridge ausgewählt ist.
Rider
Lösung src/DataBridge.slnx öffnen.
Run-Profil DataBridge auswählen, dann auf das Käfer-Symbol (Debug) klicken bzw.
Shift+F9.
Breakpoints setzen, z. B. im Connector des Systems, das gerade untersucht wird.
Visual Studio
src/DataBridge.slnx öffnen, DataBridge als Startprojekt setzen.
Profil DataBridge im Dropdown neben dem Play-Pfeil wählen, dann F5.
Breakpoints setzen.
VS Code
Terminal in src/DataBridge öffnen, func start ausführen (liest local.settings.json
automatisch).
Mit C# Dev Kit: Debug: Attach to .NET Process auf den laufenden func-Prozess. Mit der
Azure Functions-Erweiterung genügt F5 direkt im Projektordner.
Breakpoints setzen.
Der MCP-Server databridge (nicht databridge-dev) aus .mcp.json zeigt per Voreinstellung auf
http://localhost:7062 — passt also automatisch, solange kein DATABRIDGE_MCP_URL gesetzt ist,
das auf eine andere Instanz zeigt.
Logging
Hier läuft Logging doppelt, weil local.settings.json einen echten
APPLICATIONINSIGHTS_CONNECTION_STRING enthält (aus sample.local.settings.json übernommen und
mit echten Werten gefüllt, siehe oben):
Direkt in der IDE-Konsole — wie im Host-Modus gegen Podman: Rider-Run-Fenster,
Visual-Studio-Ausgabefenster bzw. das func start-Terminal in VS Code. host.json setzt
Trace für DataBridge/Function, also die volle Ausführlichkeit, unabhängig von Application
Insights.
In der echten Application Insights-Instanz — mit denselben Regeln wie in Produktion:
gepuffertes Sampling, TELEMETRY_INFO_SAMPLE_RATE, TELEMETRY_CONTROL, siehe
Betrieb → Logging. Trace-Einträge erreichen Application Insights
dabei nie — nur die IDE-Konsole zeigt sie.
Läuft im selben Application-Insights-Workspace wie andere Instanzen
Die lokal debuggte Instanz sendet in dieselbe Application-Insights-Ressource wie jede andere
Instanz, die denselben Connection-String nutzt (z. B. die produktiv laufende Function App). In der
Transaktionssuche/Log-Analyse lässt sich die eigene Ausführung am ehesten über den Zeitpunkt und —
sofern gesetzt — cloud_RoleInstance von den übrigen unterscheiden. Für eine gezielte Analyse
TELEMETRY_CONTROL=TRACE_ALL in local.settings.json setzen, den Job manuell auslösen und danach
wieder zurücksetzen (siehe Betrieb → Logging → Tipps für die Fehlersuche).
GetChanges/Process*QueueItems steuern
Anders als im Podman-Host-Modus gibt es hier keinen up.sh-Schalter — die App-Settings stehen
direkt in local.settings.json und werden von Hand gesetzt:
"true" deaktiviert die jeweilige Funktion, "false" bzw. Weglassen aktiviert sie. Vor dem
Debuggen bewusst entscheiden:
Nur einen bestimmten Job untersuchen, sonst nichts verarbeiten: alle vier auf "true"
lassen und stattdessen gezielt POST /api/ExecuteJob aufrufen (löst GetChanges für einen
einzelnen Job aus, unabhängig vom Timer).
Den echten Timer-/Queue-Ablauf beobachten: die passenden Einträge auf "false" setzen. Das
bedeutet aber, dass die lokal debuggte Instanz zusätzlich zu jeder produktiv laufenden Instanz
derselben Umgebung Änderungen erkennt bzw. Queue-Nachrichten verarbeitet.
Vorsicht bei geteilten Umgebungen
Anders als beim Podman-Stack gibt es hier keinen Reset auf Knopfdruck und keine Testdaten, die
sich beliebig zurücksetzen lassen. Änderungen (Upserts, Watermarks, Queue-Nachrichten) wirken sich
auf die echte Zielumgebung aus und sind für andere sichtbar, die dieselbe Umgebung nutzen. Vor dem
Aktivieren von GetChanges/Process*QueueItems mit dem Team abstimmen, wer die Umgebung gerade
sonst verwendet — im Zweifel zuerst gegen Podman reproduzieren, bevor man gegen eine
geteilte Cloud-Umgebung debuggt.
Podman- und Cloud-Settings parallel halten
src/DataBridge/local.settings.json ist für beide Debug-Varianten dieselbe Datei — wer öfter
zwischen Podman-Host-Modus und Cloud-Debugging wechselt, verliert sonst bei jedem up.sh --host
die mühsam eingetragenen echten Zugangsdaten (siehe Warnhinweis oben). Zwei Optionen:
Eigene Kopie + Copy-Befehl (empfohlen, geringer Aufwand). Einmalig eine zweite,
selbst gepflegte Datei anlegen, die up.sh/up.ps1 nie anfassen — z. B.
src/DataBridge/local.settings.cloud.json (Kopie von sample.local.settings.json mit echten
Werten, siehe oben). Diese Namensform (local.settings.*.json) ist bereits über
src/DataBridge/.gitignore ausgeschlossen, landet also nicht versehentlich in einem Commit. Vor
dem Cloud-Debuggen einmal darüberkopieren:
Der Rückweg ist kein manueller Schritt: Der nächste dev/up.sh --host/dev\up.ps1 -HostMode
überschreibt local.settings.json ohnehin automatisch wieder aus der Podman-Vorlage
(dev/local.settings.dev.json).
Getrennter Checkout/Git-Worktree. Für häufigen, unterbrechungsfreien Wechsel: einen zweiten
Checkout des Repos ausschließlich fürs Cloud-Debuggen anlegen, mit eigener, dauerhaft gefüllter
local.settings.json, komplett getrennt vom Ordner, in dem dev/up.sh --host läuft. Mehr
Verwaltungsaufwand beim Einrichten, dafür kein Kopieren mehr im Alltag — einfach die passende IDE
im jeweils anderen Ordner öffnen.