package logging import ( "context" "time" ) // AuditAction represents a security-relevant action. type AuditAction string const ( AuditActionCreate AuditAction = "create" AuditActionRead AuditAction = "read" AuditActionUpdate AuditAction = "update" AuditActionDelete AuditAction = "delete" AuditActionLogin AuditAction = "login" AuditActionLogout AuditAction = "logout" AuditActionGrant AuditAction = "grant" AuditActionRevoke AuditAction = "revoke" AuditActionExecute AuditAction = "execute" AuditActionDeploy AuditAction = "deploy" AuditActionProvision AuditAction = "provision" AuditActionDeprovision AuditAction = "deprovision" ) // AuditResult represents the outcome of an audited action. type AuditResult string const ( AuditResultSuccess AuditResult = "success" AuditResultFailure AuditResult = "failure" AuditResultDenied AuditResult = "denied" ) // AuditEvent represents a security audit event. type AuditEvent struct { // Timestamp is when the event occurred. Timestamp time.Time // Action is what was attempted. Action AuditAction // Resource is what the action was performed on. Resource string // ResourceID identifies the specific resource. ResourceID string // Result is the outcome of the action. Result AuditResult // UserID is who performed the action (empty for system actions). UserID string // APIKeyID is the API key used (empty for internal actions). APIKeyID string // RequestID correlates to the HTTP request. RequestID string // Details contains additional context. Details map[string]any } // AuditLogger logs security-relevant events. // Audit logs are always written regardless of log level. type AuditLogger struct { logger *Logger } // NewAuditLogger creates a new audit logger. func NewAuditLogger(l *Logger) *AuditLogger { return &AuditLogger{ logger: l.WithComponent("audit"), } } // Log logs an audit event. func (a *AuditLogger) Log(ctx context.Context, event AuditEvent) { if event.Timestamp.IsZero() { event.Timestamp = time.Now() } // Build attributes attrs := []any{ FieldAuditAction, string(event.Action), FieldAuditResource, event.Resource, FieldAuditResult, string(event.Result), "resource_id", event.ResourceID, "timestamp", event.Timestamp.Format(time.RFC3339), } if event.UserID != "" { attrs = append(attrs, FieldUserID, event.UserID) } if event.APIKeyID != "" { attrs = append(attrs, FieldAPIKeyID, event.APIKeyID) } if event.RequestID != "" { attrs = append(attrs, FieldRequestID, event.RequestID) } // Add details as individual fields for k, v := range event.Details { attrs = append(attrs, k, v) } // Audit logs are always Info level (never skipped) a.logger.Info("audit event", attrs...) } // LogAction is a convenience method for logging simple actions. func (a *AuditLogger) LogAction(ctx context.Context, action AuditAction, resource, resourceID string, result AuditResult) { // Extract context values if available l := FromContext(ctx) event := AuditEvent{ Action: action, Resource: resource, ResourceID: resourceID, Result: result, } // Try to extract request ID from the context logger // This works because the middleware adds these fields a.Log(ctx, event) _ = l // We might want to extract fields from context in the future } // LogSuccess logs a successful action. func (a *AuditLogger) LogSuccess(ctx context.Context, action AuditAction, resource, resourceID string) { a.LogAction(ctx, action, resource, resourceID, AuditResultSuccess) } // LogFailure logs a failed action. func (a *AuditLogger) LogFailure(ctx context.Context, action AuditAction, resource, resourceID string) { a.LogAction(ctx, action, resource, resourceID, AuditResultFailure) } // LogDenied logs a denied action. func (a *AuditLogger) LogDenied(ctx context.Context, action AuditAction, resource, resourceID string) { a.LogAction(ctx, action, resource, resourceID, AuditResultDenied) }