Documentation
¶
Overview ¶
Package audit provides a unified audit logging toolkit for Go services. It supports multiple storage backends (file, database, Redis) and async writing.
Index ¶
- Constants
- func MaskDestination(dest string, channel string) string
- func MaskEmail(email string) string
- func MaskIP(ip string) string
- func MaskPhone(phone string) string
- func MaskString(s string, keepChars int) string
- type Config
- type DatabaseConfig
- type DatabaseStorage
- type EventType
- type FileStorage
- type Logger
- func (l *Logger) GetStats() *Stats
- func (l *Logger) Log(ctx context.Context, record *Record)
- func (l *Logger) LogAccess(ctx context.Context, eventType EventType, userID, resource string, ...)
- func (l *Logger) LogAuth(ctx context.Context, eventType EventType, userID string, result Result, ...)
- func (l *Logger) LogChallenge(ctx context.Context, eventType EventType, challengeID, userID string, ...)
- func (l *Logger) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
- func (l *Logger) SetLogCallback(fn func(record *Record))
- func (l *Logger) Stop() error
- type MultiStorage
- type NoopStorage
- type QueryFilter
- func (f *QueryFilter) Normalize()
- func (f *QueryFilter) WithChallengeID(challengeID string) *QueryFilter
- func (f *QueryFilter) WithChannel(channel string) *QueryFilter
- func (f *QueryFilter) WithEventType(eventType string) *QueryFilter
- func (f *QueryFilter) WithIP(ip string) *QueryFilter
- func (f *QueryFilter) WithLimit(limit int) *QueryFilter
- func (f *QueryFilter) WithOffset(offset int) *QueryFilter
- func (f *QueryFilter) WithResult(result string) *QueryFilter
- func (f *QueryFilter) WithSessionID(sessionID string) *QueryFilter
- func (f *QueryFilter) WithTimeRange(startTime, endTime int64) *QueryFilter
- func (f *QueryFilter) WithUserID(userID string) *QueryFilter
- type Record
- func (r *Record) Copy() *Record
- func (r *Record) SetTimestamp(ts int64) *Record
- func (r *Record) ToJSON() ([]byte, error)
- func (r *Record) WithChallengeID(challengeID string) *Record
- func (r *Record) WithChannel(channel string) *Record
- func (r *Record) WithDestination(destination string) *Record
- func (r *Record) WithDuration(durationMS int64) *Record
- func (r *Record) WithIP(ip string) *Record
- func (r *Record) WithMetadata(key string, value interface{}) *Record
- func (r *Record) WithProvider(provider, messageID string) *Record
- func (r *Record) WithPurpose(purpose string) *Record
- func (r *Record) WithReason(reason string) *Record
- func (r *Record) WithRequestID(requestID string) *Record
- func (r *Record) WithResource(resource string) *Record
- func (r *Record) WithSessionID(sessionID string) *Record
- func (r *Record) WithTraceID(traceID string) *Record
- func (r *Record) WithUserAgent(ua string) *Record
- func (r *Record) WithUserID(userID string) *Record
- type RecordOption
- func WithRecordChannel(channel string) RecordOption
- func WithRecordDestination(dest string) RecordOption
- func WithRecordIP(ip string) RecordOption
- func WithRecordMetadata(key string, value interface{}) RecordOption
- func WithRecordProvider(provider, messageID string) RecordOption
- func WithRecordPurpose(purpose string) RecordOption
- func WithRecordReason(reason string) RecordOption
- func WithRecordRequestID(requestID string) RecordOption
- func WithRecordTraceID(traceID string) RecordOption
- func WithRecordUserAgent(ua string) RecordOption
- type RedisConfig
- type RedisStorage
- func (s *RedisStorage) Cleanup(ctx context.Context) (int64, error)
- func (s *RedisStorage) Client() *redis.Client
- func (s *RedisStorage) Close() error
- func (s *RedisStorage) KeyPrefix() string
- func (s *RedisStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
- func (s *RedisStorage) TTL() time.Duration
- func (s *RedisStorage) Write(ctx context.Context, record *Record) error
- type Result
- type Stats
- type Storage
- type StorageOptions
- type StorageType
- type Writer
- type WriterConfig
Constants ¶
const MaxRecordJSONSize = 1 << 20
MaxRecordJSONSize is the maximum allowed size for RecordFromJSON input (1MB). Larger payloads are rejected to avoid DoS from unbounded unmarshaling.
Variables ¶
This section is empty.
Functions ¶
func MaskDestination ¶
MaskDestination masks a destination (phone or email) based on channel
func MaskString ¶
MaskString masks a string, keeping first and last n characters
Types ¶
type Config ¶
type Config struct {
// Enabled controls whether audit logging is enabled
Enabled bool
// MaskDestination controls whether destinations (phone/email) should be masked
MaskDestination bool
// TTL for Redis/cache storage (0 means use storage default)
TTL time.Duration
// Writer configuration (for async writing)
Writer *WriterConfig
// OnEnqueueFailed, if set, is invoked when an async record is dropped
// because the writer queue is full. Callers can use it to increment a
// drop-counter metric or emit an alert so silent audit loss is observable.
OnEnqueueFailed func(record *Record)
// OnWriteFailed, if set, is invoked when a record fails to persist to the
// backing storage.
OnWriteFailed func(record *Record, err error)
}
Config holds configuration for the audit logger
type DatabaseConfig ¶
type DatabaseConfig struct {
TableName string // Custom table name (default: "audit_logs")
}
DatabaseConfig holds configuration for database storage
func DefaultDatabaseConfig ¶
func DefaultDatabaseConfig() *DatabaseConfig
DefaultDatabaseConfig returns default database configuration
type DatabaseStorage ¶
type DatabaseStorage struct {
// contains filtered or unexported fields
}
DatabaseStorage implements Storage interface for database-based audit logging Supports PostgreSQL and MySQL
func NewDatabaseStorage ¶
func NewDatabaseStorage(databaseURL string) (*DatabaseStorage, error)
NewDatabaseStorage creates a new database storage instance
func NewDatabaseStorageFromDB ¶
func NewDatabaseStorageFromDB(db *sql.DB, dbType string, config *DatabaseConfig) (*DatabaseStorage, error)
NewDatabaseStorageFromDB creates a new database storage from existing *sql.DB
func NewDatabaseStorageWithConfig ¶
func NewDatabaseStorageWithConfig(databaseURL string, config *DatabaseConfig) (*DatabaseStorage, error)
NewDatabaseStorageWithConfig creates a new database storage instance with config
func (*DatabaseStorage) Close ¶
func (s *DatabaseStorage) Close() error
Close closes the database connection
func (*DatabaseStorage) DB ¶
func (s *DatabaseStorage) DB() *sql.DB
DB returns the underlying database connection
func (*DatabaseStorage) DBType ¶
func (s *DatabaseStorage) DBType() string
DBType returns the database type
func (*DatabaseStorage) Query ¶
func (s *DatabaseStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
Query queries audit records from the database
type EventType ¶
type EventType string
EventType represents the type of audit event
const ( // Challenge lifecycle events EventChallengeCreated EventType = "challenge_created" EventChallengeVerified EventType = "challenge_verified" EventChallengeRevoked EventType = "challenge_revoked" EventChallengeExpired EventType = "challenge_expired" // Send events EventSendSuccess EventType = "send_success" EventSendFailed EventType = "send_failed" // Verification events EventVerificationSuccess EventType = "verification_success" EventVerificationFailed EventType = "verification_failed" // Authentication events EventLoginSuccess EventType = "login_success" EventLoginFailed EventType = "login_failed" EventLogout EventType = "logout" EventSessionCreate EventType = "session_create" EventSessionExpire EventType = "session_expire" // Authorization events EventAccessGranted EventType = "access_granted" EventAccessDenied EventType = "access_denied" // User management events EventUserCreated EventType = "user_created" EventUserUpdated EventType = "user_updated" EventUserDeleted EventType = "user_deleted" EventUserLocked EventType = "user_locked" EventUserUnlocked EventType = "user_unlocked" // Rate limit events EventRateLimited EventType = "rate_limited" // Generic events EventCustom EventType = "custom" )
Common event types for OTP/Authentication services
type FileStorage ¶
type FileStorage struct {
// contains filtered or unexported fields
}
FileStorage implements Storage interface for file-based audit logging Uses JSON Lines format (one JSON object per line)
func NewFileStorage ¶
func NewFileStorage(filePath string) (*FileStorage, error)
NewFileStorage creates a new file storage instance. filePath must come from trusted configuration only; do not pass user-controlled paths (path traversal or symlinks could write audit logs to unintended locations).
func (*FileStorage) Close ¶
func (s *FileStorage) Close() error
Close closes the file and releases resources
func (*FileStorage) FilePath ¶
func (s *FileStorage) FilePath() string
FilePath returns the file path
func (*FileStorage) Query ¶
func (s *FileStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
Query reads audit records from the file matching the filter Note: File storage query is simple and may be slow for large files For production use, consider using database storage
func (*FileStorage) Rotate ¶
func (s *FileStorage) Rotate() error
Rotate rotates the audit log file Creates a new file with timestamp suffix and reopens the main file
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger handles audit logging with optional async writing
func NewLoggerWithWriter ¶
NewLoggerWithWriter creates a new audit logger with async writer
func (*Logger) Log ¶
Log records an audit event. The provided record is never modified; a copy is made for masking and writing, so the caller may safely reuse the record.
func (*Logger) LogAccess ¶
func (l *Logger) LogAccess(ctx context.Context, eventType EventType, userID, resource string, result Result, opts ...RecordOption)
LogAccess logs an access control event
func (*Logger) LogAuth ¶
func (l *Logger) LogAuth(ctx context.Context, eventType EventType, userID string, result Result, opts ...RecordOption)
LogAuth logs an authentication event
func (*Logger) LogChallenge ¶
func (l *Logger) LogChallenge(ctx context.Context, eventType EventType, challengeID, userID string, result Result, opts ...RecordOption)
LogChallenge logs a challenge-related event
func (*Logger) Query ¶
Query queries audit records from storage. Filter is normalized (limit/offset) before being passed to storage so behavior is consistent across backends.
func (*Logger) SetLogCallback ¶
SetLogCallback sets a callback that is called for each log entry Useful for also logging to standard logger
type MultiStorage ¶
type MultiStorage struct {
// contains filtered or unexported fields
}
MultiStorage combines multiple storage backends
func NewMultiStorage ¶
func NewMultiStorage(storages ...Storage) *MultiStorage
NewMultiStorage creates a storage that writes to multiple backends
func (*MultiStorage) Query ¶
func (m *MultiStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
Query queries from the first storage backend
func (*MultiStorage) Storages ¶
func (m *MultiStorage) Storages() []Storage
Storages returns all storage backends
type NoopStorage ¶
type NoopStorage struct{}
NoopStorage is a no-op storage that discards all records
func (*NoopStorage) Query ¶
func (s *NoopStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
Query returns empty results
type QueryFilter ¶
type QueryFilter struct {
// Filter by event type
EventType string `json:"event_type,omitempty"`
// Filter by subject
UserID string `json:"user_id,omitempty"`
ChallengeID string `json:"challenge_id,omitempty"`
SessionID string `json:"session_id,omitempty"`
// Filter by channel and result
Channel string `json:"channel,omitempty"`
Result string `json:"result,omitempty"`
// Time range filters (Unix timestamps)
StartTime int64 `json:"start_time,omitempty"`
EndTime int64 `json:"end_time,omitempty"`
// Filter by IP
IP string `json:"ip,omitempty"`
// Pagination
Limit int `json:"limit,omitempty"` // Maximum number of records (default: 100)
Offset int `json:"offset,omitempty"` // Offset for pagination (default: 0)
}
QueryFilter defines filter criteria for querying audit records
func DefaultQueryFilter ¶
func DefaultQueryFilter() *QueryFilter
DefaultQueryFilter returns a default query filter with sensible defaults
func (*QueryFilter) Normalize ¶
func (f *QueryFilter) Normalize()
Normalize ensures filter has valid values
func (*QueryFilter) WithChallengeID ¶
func (f *QueryFilter) WithChallengeID(challengeID string) *QueryFilter
WithChallengeID sets the challenge ID filter
func (*QueryFilter) WithChannel ¶
func (f *QueryFilter) WithChannel(channel string) *QueryFilter
WithChannel sets the channel filter
func (*QueryFilter) WithEventType ¶
func (f *QueryFilter) WithEventType(eventType string) *QueryFilter
WithEventType sets the event type filter
func (*QueryFilter) WithIP ¶
func (f *QueryFilter) WithIP(ip string) *QueryFilter
WithIP sets the IP filter
func (*QueryFilter) WithLimit ¶
func (f *QueryFilter) WithLimit(limit int) *QueryFilter
WithLimit sets the limit
func (*QueryFilter) WithOffset ¶
func (f *QueryFilter) WithOffset(offset int) *QueryFilter
WithOffset sets the offset
func (*QueryFilter) WithResult ¶
func (f *QueryFilter) WithResult(result string) *QueryFilter
WithResult sets the result filter
func (*QueryFilter) WithSessionID ¶
func (f *QueryFilter) WithSessionID(sessionID string) *QueryFilter
WithSessionID sets the session ID filter
func (*QueryFilter) WithTimeRange ¶
func (f *QueryFilter) WithTimeRange(startTime, endTime int64) *QueryFilter
WithTimeRange sets the time range filter
func (*QueryFilter) WithUserID ¶
func (f *QueryFilter) WithUserID(userID string) *QueryFilter
WithUserID sets the user ID filter
type Record ¶
type Record struct {
// Event identification
EventType EventType `json:"event_type"`
EventID string `json:"event_id,omitempty"` // Unique event identifier
// Subject identification
UserID string `json:"user_id,omitempty"`
ChallengeID string `json:"challenge_id,omitempty"`
SessionID string `json:"session_id,omitempty"`
// Event details
Channel string `json:"channel,omitempty"` // sms, email, push, etc.
Destination string `json:"destination,omitempty"` // May be masked
Purpose string `json:"purpose,omitempty"` // login, reset, bind, etc.
Resource string `json:"resource,omitempty"` // Accessed resource
// Result
Result Result `json:"result"`
Reason string `json:"reason,omitempty"` // Failure reason
// Provider info (for external services)
Provider string `json:"provider,omitempty"`
ProviderMessageID string `json:"provider_message_id,omitempty"`
// Request context
IP string `json:"ip,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
RequestID string `json:"request_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
// Timing
Timestamp int64 `json:"timestamp"` // Unix timestamp
DurationMS int64 `json:"duration_ms,omitempty"` // Operation duration
// Extensible metadata
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
Record represents an audit log entry
func RecordFromJSON ¶
RecordFromJSON deserializes a record from JSON. Input larger than MaxRecordJSONSize is rejected to prevent memory exhaustion. Metadata is unmarshaled as map[string]interface{} without depth limit; do not pass untrusted JSON with deeply nested metadata.
func (*Record) Copy ¶ added in v1.1.0
Copy returns a shallow copy of the record. The caller's record is unchanged. Used internally so Log() can mask and write without mutating the original.
func (*Record) SetTimestamp ¶
SetTimestamp sets the timestamp (useful for testing)
func (*Record) WithChallengeID ¶
WithChallengeID sets the challenge ID
func (*Record) WithChannel ¶
WithChannel sets the channel (sms, email, etc.)
func (*Record) WithDestination ¶
WithDestination sets the destination (phone, email, etc.)
func (*Record) WithDuration ¶
WithDuration sets the operation duration in milliseconds
func (*Record) WithMetadata ¶
WithMetadata sets custom metadata
func (*Record) WithProvider ¶
WithProvider sets the provider info
func (*Record) WithPurpose ¶
WithPurpose sets the purpose (login, reset, etc.)
func (*Record) WithReason ¶
WithReason sets the failure reason
func (*Record) WithRequestID ¶
WithRequestID sets the request ID
func (*Record) WithResource ¶
WithResource sets the accessed resource
func (*Record) WithSessionID ¶
WithSessionID sets the session ID
func (*Record) WithTraceID ¶
WithTraceID sets the trace ID
func (*Record) WithUserAgent ¶
WithUserAgent sets the user agent
func (*Record) WithUserID ¶
WithUserID sets the user ID
type RecordOption ¶
type RecordOption func(*Record)
RecordOption is a function that modifies a record
func WithRecordChannel ¶
func WithRecordChannel(channel string) RecordOption
WithRecordChannel returns an option that sets the channel
func WithRecordDestination ¶
func WithRecordDestination(dest string) RecordOption
WithRecordDestination returns an option that sets the destination
func WithRecordIP ¶
func WithRecordIP(ip string) RecordOption
WithRecordIP returns an option that sets the IP
func WithRecordMetadata ¶
func WithRecordMetadata(key string, value interface{}) RecordOption
WithRecordMetadata returns an option that sets metadata
func WithRecordProvider ¶
func WithRecordProvider(provider, messageID string) RecordOption
WithRecordProvider returns an option that sets the provider
func WithRecordPurpose ¶
func WithRecordPurpose(purpose string) RecordOption
WithRecordPurpose returns an option that sets the purpose
func WithRecordReason ¶
func WithRecordReason(reason string) RecordOption
WithRecordReason returns an option that sets the reason
func WithRecordRequestID ¶
func WithRecordRequestID(requestID string) RecordOption
WithRecordRequestID returns an option that sets the request ID
func WithRecordTraceID ¶
func WithRecordTraceID(traceID string) RecordOption
WithRecordTraceID returns an option that sets the trace ID
func WithRecordUserAgent ¶
func WithRecordUserAgent(ua string) RecordOption
WithRecordUserAgent returns an option that sets the user agent
type RedisConfig ¶
type RedisConfig struct {
KeyPrefix string // Key prefix (default: "audit:")
TTL time.Duration // Time-to-live for records (default: 7 days)
}
RedisConfig holds configuration for Redis storage
func DefaultRedisConfig ¶
func DefaultRedisConfig() *RedisConfig
DefaultRedisConfig returns default Redis configuration
type RedisStorage ¶
type RedisStorage struct {
// contains filtered or unexported fields
}
RedisStorage implements Storage interface for Redis-based audit logging Suitable for short-term storage and quick access
func NewRedisStorage ¶
func NewRedisStorage(client *redis.Client) *RedisStorage
NewRedisStorage creates a new Redis storage instance
func NewRedisStorageWithConfig ¶
func NewRedisStorageWithConfig(client *redis.Client, config *RedisConfig) *RedisStorage
NewRedisStorageWithConfig creates a new Redis storage instance with config
func (*RedisStorage) Cleanup ¶
func (s *RedisStorage) Cleanup(ctx context.Context) (int64, error)
Cleanup removes expired keys from the index
func (*RedisStorage) Client ¶
func (s *RedisStorage) Client() *redis.Client
Client returns the underlying Redis client
func (*RedisStorage) KeyPrefix ¶
func (s *RedisStorage) KeyPrefix() string
KeyPrefix returns the key prefix
func (*RedisStorage) Query ¶
func (s *RedisStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
Query queries audit records from Redis
type Storage ¶
type Storage interface {
// Write writes an audit record to the storage backend
Write(ctx context.Context, record *Record) error
// Query queries audit records based on filter criteria
// Returns records matching the filter, ordered by timestamp (newest first)
Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)
// Close closes the storage connection and releases resources
Close() error
}
Storage defines the interface for audit log storage backends
func NewStorageFromType ¶
func NewStorageFromType(storageType StorageType, opts *StorageOptions) (Storage, error)
NewStorageFromType creates a storage instance based on type
type StorageOptions ¶
type StorageOptions struct {
// File storage options
FilePath string
// Database storage options
DatabaseURL string
TableName string
// Redis storage options
RedisClient *redis.Client
RedisPrefix string
RedisTTL time.Duration
}
StorageOptions holds options for creating storage
type StorageType ¶
type StorageType string
StorageType represents the type of storage backend
const ( StorageTypeFile StorageType = "file" StorageTypeDatabase StorageType = "database" StorageTypeDB StorageType = "db" // Alias for database StorageTypeRedis StorageType = "redis" StorageTypeNone StorageType = "none" )
func ParseStorageType ¶
func ParseStorageType(s string) StorageType
ParseStorageType parses a storage type string
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer handles asynchronous writing of audit records to persistent storage
func NewWriter ¶
func NewWriter(storage Storage, config *WriterConfig) *Writer
NewWriter creates a new asynchronous audit writer
func (*Writer) Enqueue ¶
Enqueue enqueues an audit record for asynchronous writing. Returns false if record is nil, the writer is stopped, or the queue is full (non-blocking). Safe to call after Stop(); will return false instead of panicking.
func (*Writer) OnEnqueueFailed ¶
OnEnqueueFailed sets a callback for when enqueue fails (queue full)
func (*Writer) OnWriteFailed ¶
OnWriteFailed sets a callback for when write fails
type WriterConfig ¶
type WriterConfig struct {
QueueSize int // Size of the async queue (default: 1000)
Workers int // Number of worker goroutines (default: 2)
StopTimeout time.Duration // Timeout for graceful shutdown (default: 10s)
}
WriterConfig holds configuration for the async writer
func DefaultWriterConfig ¶
func DefaultWriterConfig() *WriterConfig
DefaultWriterConfig returns default writer configuration