// Package protolog implementiert ING-08: strukturiertes Logging für // IMAP-/POP3-/SMTP-Sessions mit Korrelations-ID (Akzeptanzkriterium 1), // Redaktion sensibler Felder (Akzeptanzkriterium 2) und ein // Diagnosewerkzeug, das eine einzelne Session anhand ihrer // Korrelations-ID aus den Logs rekonstruiert (Akzeptanzkriterium 3, // diagnose.go). package protolog import ( "context" "crypto/rand" "encoding/hex" "log/slog" ) // NewCorrelationID erzeugt eine zufällige, session-eindeutige // Korrelations-ID. func NewCorrelationID() string { buf := make([]byte, 8) // crypto/rand.Read schlägt praktisch nie fehl; ein Nullwert würde // höchstens zu einer unwahrscheinlichen ID-Kollision führen, kein // Sicherheitsproblem für ein reines Diagnosemerkmal. _, _ = rand.Read(buf) return hex.EncodeToString(buf) } // SessionLogger loggt strukturierte Ereignisse EINER Verbindung mit // fester Korrelations-ID über deren gesamte Dauer (Akzeptanzkriterium // 1). Ein SessionLogger mit logger == nil ist sicher benutzbar und // loggt nichts (Standard für Server ohne konfigurierten Logger). type SessionLogger struct { logger *slog.Logger correlationID string protocol string } // NewSessionLogger erstellt einen SessionLogger mit frischer // Korrelations-ID. logger darf nil sein (Logging dann deaktiviert). func NewSessionLogger(logger *slog.Logger, protocol string) *SessionLogger { return &SessionLogger{logger: logger, correlationID: NewCorrelationID(), protocol: protocol} } // CorrelationID liefert die Korrelations-ID dieser Session. func (l *SessionLogger) CorrelationID() string { if l == nil { return "" } return l.correlationID } // Event loggt EIN strukturiertes Ereignis mit correlation_id und // protocol als festen Feldern. attrs dürfen NIE Zugangsdaten oder // Nachrichteninhalte enthalten — siehe RedactCommandLine für // Kommandozeilen (Akzeptanzkriterium 2). func (l *SessionLogger) Event(ctx context.Context, event string, attrs ...slog.Attr) { if l == nil || l.logger == nil { return } all := make([]slog.Attr, 0, len(attrs)+2) all = append(all, slog.String("correlation_id", l.correlationID), slog.String("protocol", l.protocol)) all = append(all, attrs...) l.logger.LogAttrs(ctx, slog.LevelInfo, event, all...) }