Wenn ein Client connection lost meldet, weiß er meist nicht, warum. Der Broker weiß es. Jeder abgelehnte Login, jeder Keep-Alive-Timeout, jede doppelte Client ID und jeder verweigerte Publish hinterlässt eine Zeile im Broker-Log — sofern das Logging so konfiguriert ist, dass es sie behält. Diese Zeilen zu lesen ist oft der schnellste Weg, einen Client zu debuggen, an den du keinen Debugger hängen kannst.
Diese Anleitung zeigt, wo die Logs liegen, welche Log-Level du einschaltest, wie du Mosquittos Connect- und Disconnect-Zeilen liest, wie du in EMQX einen einzelnen Client tracest und wie aus einer Log-Zeile eine Lösung wird.
Wo die Logs liegen
| Setup | Wo du suchst |
|---|---|
| Mosquitto aus einem Linux-Paket | /var/log/mosquitto/mosquitto.log oder journalctl -u mosquitto -f |
| Mosquitto in Docker | docker logs -f mosquitto (mit log_dest stdout) |
| EMQX | Konsolenausgabe in Docker (docker logs -f emqx); Dateien im Verzeichnis log/ der Installation |
| HiveMQ | log/hivemq.log, dazu log/event.log für Connect- und Disconnect-Ereignisse der Clients |
| Managed Broker | Meist keine rohen Broker-Logs — verlass dich auf Reason Codes und Monitoring (siehe unten) |
Bleibt die Mosquitto-Logdatei leer, such in der Startausgabe nach Error: Unable to open log file /var/log/mosquitto/mosquitto.log for writing. — der Broker-Benutzer darf nicht in das Verzeichnis schreiben. Das Logging-Setup im Detail beschreibt Mosquitto-Logging.
Das richtige Log-Level einschalten
Mosquittos Standard-Logging verbirgt das meiste, was du zum Client-Debugging brauchst. Eine sinnvolle Konfiguration während der Untersuchung:
log_dest stdout
log_dest file /var/log/mosquitto/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information
log_type subscribe
log_type unsubscribe
# Jedes ein- und ausgehende Paket — sehr gesprächig, nur beim Debuggen aktivieren
log_type debug
connection_messages true
log_timestamp true
log_timestamp_format %Y-%m-%dT%H:%M:%SStarte den Broker nach Änderungen an den Logging-Optionen neu. connection_messages true erzeugt die Connect- und Disconnect-Zeilen, auf denen fast diese ganze Anleitung aufbaut, und ein lesbares Zeitstempelformat erspart dir das Umrechnen von Unix-Sekunden. Eine vollständige Konfiguration erzeugst du mit dem mosquitto.conf-Generator.
Bei EMQX 5 erhöhst du das Konsolen-Level eines Containers mit der Umgebungsvariable EMQX_LOG__CONSOLE__LEVEL=debug oder änderst es zur Laufzeit:
emqx ctl log set-level debug
# ...Problem reproduzieren...
emqx ctl log set-level warningMosquittos Connect-Zeilen lesen
Eine erfolgreiche Verbindung erzeugt zwei Zeilen:
2026-10-17T09:12:01: New connection from 192.168.1.20:52144 on port 1883.
2026-10-17T09:12:01: New client connected from 192.168.1.20:52144 as sensor-01 (p2, c1, k60, u'alice').Die Klammer am Ende beschreibt das CONNECT-Paket:
| Feld | Bedeutung |
|---|---|
p2 | Protokoll: p2 = MQTT 3.1.1, p5 = MQTT 5 (p1 wäre das alte 3.1) |
c1 | Clean Session / Clean Start: c1 = neue Session, c0 = persistente Session fortsetzen |
k60 | Keep-Alive-Intervall in Sekunden |
u'alice' | Vom Client gesendeter Benutzername (fehlt, wenn keiner gesendet wurde) |
Diese vier Felder beantworten mehrere Fragen auf einmal: Nutzt der Client wirklich die Protokollversion, die du annimmst? Fordert er eine persistente Session an? Welches Keep-Alive hat er tatsächlich gesetzt?
Disconnect-Zeilen und ihre Bedeutung
Die Zeile, die Mosquitto beim Abgang eines Clients schreibt, ist die nützlichste im ganzen Log:
| Log-Zeile | Ursache | Lösung |
|---|---|---|
Client sensor-01 disconnected. | Sauberes DISCONNECT vom Client | Nichts — gewollt |
Client sensor-01 closed its connection. | TCP ohne DISCONNECT geschlossen | Client abgestürzt, beendet oder ohne Strom |
Client sensor-01 has exceeded timeout, disconnecting. | Kein Paket für 1,5 × Keep-Alive | Client-Schleife, Schlaflogik oder NAT-Timeout korrigieren — siehe Keep-Alive-Timeout |
Client sensor-01 already connected, closing old connection. | Ein zweiter Client nutzt dieselbe Client ID | Eindeutige IDs pro Gerät — siehe doppelte Client ID |
Client sensor-01 disconnected, not authorised. | Falsche Zugangsdaten oder keine Berechtigung zum Verbinden | Benutzername, Passwort und Auth-Plugin prüfen |
Client sensor-01 disconnected due to protocol error. | Fehlerhaftes oder unerwartetes Paket | Bibliotheksfehler, falsche Protokollversion oder reines MQTT auf einem WebSocket-Listener |
Socket error on client sensor-01, disconnecting. | Verbindung auf Netzwerkebene abgerissen | Netzausfall, TLS-Fehler oder RST einer Middlebox |
Outgoing messages are being dropped for client sensor-01. | Die Queue des Clients ist voll | Langsamer Consumer — Limits erhöhen oder schneller konsumieren |
Wechseln sich alle paar Sekunden zwei Zeilen mit already connected, closing old connection ab, streiten zwei Geräte um eine Client ID und verbinden sich in einer Schleife neu. Eindeutige IDs erzeugst du mit dem Client-ID-Generator.
Eine Session auf Debug-Level verfolgen
Mit log_type debug protokolliert Mosquitto jedes Paket. Ein Subscriber und ein Publisher auf einem Test-Topic sehen ungefähr so aus (Mosquitto 2.x, gekürzt):
Received SUBSCRIBE from sub-01
testmqtt/logs/# (QoS 1)
sub-01 1 testmqtt/logs/#
Sending SUBACK to sub-01
Received PUBLISH from pub-01 (d0, q1, r0, m1, 'testmqtt/logs/a', ... (5 bytes))
Sending PUBACK to pub-01 (m1, rc0)
Sending PUBLISH to sub-01 (d0, q1, r0, m1, 'testmqtt/logs/a', ... (5 bytes))
Received PUBACK from sub-01 (Mid: 1, RC:0)Die Klammer bei PUBLISH-Zeilen liest sich so: d = DUP-Flag, q = QoS, r = Retain, m = Message ID, danach Topic und Payload-Größe. Siehst du Received PUBLISH, aber kein Sending PUBLISH, hat der Broker die Nachricht angenommen, aber keine passende Subscription gefunden — prüfe den Topic-Filter mit dem Topic-Matcher. Eine Denied PUBLISH-Zeile bedeutet, dass eine ACL blockiert hat; diesen Fall behandelt ACL verweigert stillschweigend.
Um einen Fall schnell nachzustellen, lass das Log in einem Terminal offen und erzeuge in einem zweiten Traffic:
# Terminal 1 — die Broker-Seite
docker logs -f mosquitto 2>&1 | grep --line-buffered -E 'sub-01|pub-01'
# Terminal 2 — die Client-Seite, mit Debug-Ausgabe der Clients
mosquitto_sub -d -h localhost -i sub-01 -t 'testmqtt/logs/#' -q 1 &
mosquitto_pub -d -h localhost -i pub-01 -t 'testmqtt/logs/a' -q 1 -m 'hello'-d lässt die Clients ihr eigenes Paket-Log ausgeben (Client sub-01 sending CONNECT, received CONNACK (0)), sodass du beide Sichten Zeile für Zeile vergleichen kannst.
Einen Client in EMQX tracen
Auf einem ausgelasteten EMQX-Broker ist globales Debug-Logging zu laut. Trace stattdessen einen einzelnen Client:
emqx ctl trace start client sensor-01 /tmp/sensor-01.log
# ...Client verbinden, publizieren und scheitern lassen...
emqx ctl trace stop client sensor-01Die Trace-Datei enthält die Connect-, Authentifizierungs-, Subscribe-, Publish- und Disconnect-Ereignisse dieses Clients, inklusive des Grundes, wenn der Broker die Verbindung schließt. Das Dashboard hat eine Log-Trace-Seite, die dasselbe tut und den Download des Ergebnisses erlaubt. Weißt du nicht, welche Client ID sich danebenbenimmt, kannst du auch nach Topic oder Client-IP tracen.
Suchen und korrelieren
Ein paar Befehle decken die meisten Log-Suchen ab:
# Alles zu einem Client in der letzten Stunde
journalctl -u mosquitto --since "1 hour ago" | grep -F 'sensor-01'
# Welche Clients laufen am häufigsten in Timeouts?
grep -o 'Client [^ ]* has exceeded timeout' /var/log/mosquitto/mosquitto.log | sort | uniq -c | sort -rn
# Unix-Zeitstempel aus einer Standard-Mosquitto-Logzeile umrechnen
date -d @1792228321 # GNU date; unter macOS: date -r 1792228321Korreliere die Broker-Zeit mit den Logs des Clients. Weichen die Uhren ab, richte beide an einem Ereignis aus, das beide Seiten sehen, etwa dem CONNACK.
Wenn es kein Broker-Log gibt
Ohne Zugriff auf die Broker-Logs baust du dasselbe Bild von der Client-Seite aus auf:
- Protokolliere jeden Reason Code, den deine Bibliothek meldet, und schlag ihn in MQTT-Reason-Codes erklärt oder im Reason-Code-Tool nach.
- Abonniere die
$SYS-Topics des Brokers, sofern verfügbar — Zähler für verbundene Clients und verworfene Nachrichten zeigen oft denselben Trend wie das Log. Siehe Monitoring mit $SYS-Topics. - Stell das Problem mit dem Online-MQTT-Client gegen denselben Broker nach. Funktioniert es dort, liegt die Ursache im Code oder Netzwerk des Geräts, nicht beim Broker.
Die Lösung im Log bestätigen
Eine Log-Zeile ist auch dein Abnahmetest. Stell nach einer Änderung das ursprüngliche Szenario nach und beobachte das Log: Die Zeile has exceeded timeout oder already connected sollte nicht mehr erscheinen, und die Zeile New client connected sollte die Parameter zeigen, die du beabsichtigt hast (Protokoll, Clean-Flag, Keep-Alive). Schalte danach das Debug-Logging wieder ab.
Der Broker sieht die Fehler aller Clients, schreibt sie aber nur auf, wenn du ihn darum bittest. Konfiguriere Connection Messages und einen lesbaren Zeitstempel, erhöhe das Level nur während der Untersuchung und lerne die Handvoll Disconnect-Zeilen — sie entsprechen fast eins zu eins der Lösung.
Häufig gestellte Fragen
Wie aktiviere ich Debug-Logging in Mosquitto?
Füge log_type debug (oder log_type all) zusammen mit einem log_dest wie stdout oder file in mosquitto.conf ein und starte den Broker neu. Debug-Logging gibt jedes Paket aus, das der Broker empfängt und sendet — schalte es wieder ab, sobald du das Problem gefunden hast.
Was bedeutet 'has exceeded timeout, disconnecting' in Mosquitto?
Der Broker hat 1,5 Keep-Alive-Intervalle lang nichts vom Client empfangen und die Verbindung geschlossen. Die Netzwerkschleife des Clients stand still, das Gerät schlief oder ein NAT-Gateway hat die Verbindung verworfen. Prüfe den Keep-Alive-Wert in der Zeile 'New client connected' (k60 heißt 60 Sekunden).
Wie sehe ich in EMQX die Logs eines einzelnen Clients?
Nutze einen Log-Trace: emqx ctl trace start client <clientid> <datei> schreibt alles zu dieser Client ID in eine Datei, und das EMQX-Dashboard bietet dasselbe auf seiner Log-Trace-Seite. Beende ihn mit emqx ctl trace stop client <clientid>.
