in go/mqtt/internal/log_utils.go [50:113]
func reflectAttr(name string, val reflect.Value) []slog.Attr {
// Ignore zero values to keep the log cleaner.
if missingValue(val) {
return nil
}
switch name {
// Paho's struct nesting is not particularly useful to log.
case "properties":
return reflectAttrs(val)
// Subscriptions are one-at-a-time for the session client.
case "subscriptions":
if subs, ok := val.Interface().([]paho.SubscribeOptions); ok {
return reflectAttrs(reflect.ValueOf(subs[0]))
}
case "topics":
if topics, ok := val.Interface().([]string); ok {
return []slog.Attr{slog.String("topic", topics[0])}
}
case "reasons":
if reasons, ok := val.Interface().([]byte); ok {
return []slog.Attr{slog.Int("reason_code", int(reasons[0]))}
}
// Fix QoS not being actually PascalCased.
case "qo_s":
return []slog.Attr{slog.Any("qos", val.Interface())}
// Do not log secrets.
case "password", "auth_data":
return nil
}
switch v := val.Interface().(type) {
case []byte:
return []slog.Attr{slog.String(name, string(v))}
case paho.UserProperties:
if len(v) == 0 {
return nil
}
attrs := make([]any, len(v))
for i, p := range v {
attrs[i] = slog.String(p.Key, p.Value)
}
return []slog.Attr{slog.Group(name, attrs...)}
}
if val.Kind() == reflect.Struct {
as := reflectAttrs(val)
if len(as) == 0 {
return nil
}
cpy := make([]any, len(as))
for i, a := range as {
cpy[i] = a
}
return []slog.Attr{slog.Group(name, cpy...)}
}
return []slog.Attr{slog.Any(name, val.Interface())}
}