Files
go-caatsm/internal/infra/log/logging.go

103 lines
3.0 KiB
Go

package log
import "go.uber.org/zap"
// ErrorType represents a coarse-grained categorisation of errors for logging and alerting.
// Typical values:
// - business: validation failures, domain rule violations, payload issues.
// - transient: network / DB / NATS glitches that may succeed on retry.
// - fatal: programming bugs, schema mismatches, or conditions that require operator action.
type ErrorType string
const (
ErrorTypeBusiness ErrorType = "business"
ErrorTypeTransient ErrorType = "transient"
ErrorTypeFatal ErrorType = "fatal"
)
// Canonical logging field names for structured logs produced by the CAATSM
// receiver. Using constants avoids scattering magic strings and keeps log
// analysis queries stable over time.
const (
FieldService = "service"
FieldTransportMsgID = "transport_msg_id"
FieldTelegramMsgID = "telegram_message_id"
FieldCategory = "category"
FieldStream = "stream"
FieldConsumer = "consumer"
FieldSubject = "subject"
FieldNATSSequence = "nats_sequence"
FieldRequestID = "request_id"
FieldTraceID = "trace_id"
FieldErrorType = "error_type"
)
// MessageFields captures the common structured logging contract for message-processing logs.
// All fields are optional; empty values will simply be skipped.
type MessageFields struct {
// Identifiers
Service string
TransportMsgID string
BusinessMsgID string
Category string
// NATS / JetStream context
Stream string
Consumer string
Subject string
JSSequence uint64
// Correlation / tracing
RequestID string
TraceID string
// Error classification
ErrorType ErrorType
}
// WithMessageContext returns a logger pre-populated with the structured fields defined in MessageFields.
// This is the primary entry point for enforcing the logging contract in the codebase.
func WithMessageContext(logger *zap.Logger, mf MessageFields) *zap.Logger {
if logger == nil {
return zap.NewNop()
}
fields := make([]zap.Field, 0, 12)
if mf.Service != "" {
fields = append(fields, zap.String(FieldService, mf.Service))
}
if mf.TransportMsgID != "" {
fields = append(fields, zap.String(FieldTransportMsgID, mf.TransportMsgID))
}
if mf.BusinessMsgID != "" {
fields = append(fields, zap.String(FieldTelegramMsgID, mf.BusinessMsgID))
}
if mf.Category != "" {
fields = append(fields, zap.String(FieldCategory, mf.Category))
}
if mf.Stream != "" {
fields = append(fields, zap.String(FieldStream, mf.Stream))
}
if mf.Consumer != "" {
fields = append(fields, zap.String(FieldConsumer, mf.Consumer))
}
if mf.Subject != "" {
fields = append(fields, zap.String(FieldSubject, mf.Subject))
}
if mf.JSSequence > 0 {
fields = append(fields, zap.Uint64(FieldNATSSequence, mf.JSSequence))
}
if mf.RequestID != "" {
fields = append(fields, zap.String(FieldRequestID, mf.RequestID))
}
if mf.TraceID != "" {
fields = append(fields, zap.String(FieldTraceID, mf.TraceID))
}
if mf.ErrorType != "" {
fields = append(fields, zap.String(FieldErrorType, string(mf.ErrorType)))
}
return logger.With(fields...)
}