fix(mqtt): log throwable-less client errors at warn (#6493)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
James RichandClaude Opus 5 authored and GitHub committed 2026-07-28 15:11:10 +00:00
1 parent 72ca3313d8
commit b49b2a791c
2 files changed
+32 -7

No files matched your search

@@ -49,12 +49,11 @@ class KermitMqttLogger(
MqttLogLevel.WARN -> logger.w(throwable) { message }
// The library reports a broker that closed the socket, an unreachable host, or Wi-Fi dropping mid-read at
// ERROR ("Read loop error: Not enough data available"), and the client then reconnects on its own. Those
// are expected for a mobile client and dominated our error tracking, so log them at WARN — diagnosable in
// a support session, but not counted as application errors.
// The library logs its connection lifecycle at ERROR — broker socket close, unreachable host, Wi-Fi drop
// and reconnects itself; broker rejections arrive without a throwable and are user config, surfaced via
// MqttProbeStatus. Neither is an app defect, so both log at WARN. Real faults carry a throwable.
MqttLogLevel.ERROR ->
if (throwable?.isExpectedConnectionFailure() == true) {
if (throwable == null || throwable.isExpectedConnectionFailure()) {
logger.w(throwable) { message }
} else {
logger.e(throwable) { message }
@@ -67,10 +67,36 @@ class KermitMqttLoggerTest {
}
@Test
fun `an error with no throwable stays an error`() {
fun `an error with no throwable is downgraded to WARN`() {
// A bare message carries no stack or type to triage, so it must not count as an application error.
mqttLogger.log(level = MqttLogLevel.ERROR, tag = "MqttClient", message = "boom", throwable = null)
assertEquals(Severity.Error, writer.entries.single().first)
assertEquals(Severity.Warn, writer.entries.single().first)
}
@Test
fun `a connection teardown log is not reported as an application error`() {
mqttLogger.log(
level = MqttLogLevel.ERROR,
tag = "MqttConnection",
message = "Fatal error — tearing down connection",
throwable = null,
)
assertEquals(Severity.Warn, writer.entries.single().first)
}
@Test
fun `a broker rejection is a user configuration problem rather than an application error`() {
// Surfaced to the user through MqttProbeStatus, so it is not an app defect.
mqttLogger.log(
level = MqttLogLevel.ERROR,
tag = "MqttConnection",
message = "Connection refused: BAD_USER_NAME_OR_PASSWORD",
throwable = null,
)
assertEquals(Severity.Warn, writer.entries.single().first)
}
@Test