audit

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

audit-kit

Go Reference Go Report Card License codecov

中文文档

A unified audit logging toolkit for Go services. This package provides audit log interfaces, multiple storage backends (file, database, Redis), async writing with worker pools, and sensitive data masking.

Features

  • Storage Interface: Unified interface for all storage backends
  • Multiple Backends: File (JSON Lines), Database (PostgreSQL/MySQL/SQLite), Redis
  • Async Writing: Worker pool for non-blocking audit logging
  • Multi-Storage: Write to multiple backends simultaneously
  • Data Masking: Automatic masking of sensitive data (email, phone, IP)
  • Fluent API: Builder pattern for constructing audit records
  • Query Support: Filter and paginate audit records
  • Extensible: Custom event types and metadata support

Installation

go get github.com/soulteary/audit-kit

Usage

Basic Audit Logging
import (
    audit "github.com/soulteary/audit-kit"
)

// Create a file storage (filePath must be from trusted config, not user input)
storage, err := audit.NewFileStorage("/var/log/audit.log")
if err != nil {
    log.Fatal(err)
}

// Create a logger with default config
logger := audit.NewLogger(storage, nil)
defer logger.Stop()

// Log an event
record := audit.NewRecord(audit.EventLoginSuccess, audit.ResultSuccess).
    WithUserID("user123").
    WithIP("192.168.1.1").
    WithUserAgent("Mozilla/5.0")

logger.Log(context.Background(), record)
// Create logger with async writer
config := audit.DefaultConfig()
config.Writer = &audit.WriterConfig{
    QueueSize: 1000,
    Workers:   4,
}

logger := audit.NewLoggerWithWriter(storage, config)
defer logger.Stop()

// Logging is now non-blocking
logger.Log(ctx, record)
Database Storage
// PostgreSQL
storage, err := audit.NewDatabaseStorage("postgres://user:pass@localhost/db")

// MySQL
storage, err := audit.NewDatabaseStorage("mysql://user:pass@tcp(localhost:3306)/db")

// SQLite (for testing)
db, _ := sql.Open("sqlite", ":memory:")
storage, err := audit.NewDatabaseStorageFromDB(db, "sqlite", nil)
Redis Storage
import "github.com/redis/go-redis/v9"

client := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})

storage := audit.NewRedisStorageWithConfig(client, &audit.RedisConfig{
    KeyPrefix: "myapp:audit:",
    TTL:       7 * 24 * time.Hour,
})
Multi-Storage (Write to Multiple Backends)
fileStorage, _ := audit.NewFileStorage("/var/log/audit.log")
redisStorage := audit.NewRedisStorage(redisClient)

multiStorage := audit.NewMultiStorage(fileStorage, redisStorage)
logger := audit.NewLogger(multiStorage, nil)
Querying Audit Records
// Build a filter
filter := audit.DefaultQueryFilter().
    WithEventType("login_success").
    WithUserID("user123").
    WithTimeRange(startTime, endTime).
    WithLimit(50)

// Query records
records, err := logger.Query(ctx, filter)
Convenience Logging Methods
// Log challenge events (OTP/verification)
logger.LogChallenge(ctx, audit.EventChallengeCreated, "ch_123", "user123", audit.ResultSuccess,
    audit.WithRecordChannel("email"),
    audit.WithRecordDestination("test@example.com"),
)

// Log authentication events
logger.LogAuth(ctx, audit.EventLoginSuccess, "user123", audit.ResultSuccess,
    audit.WithRecordIP("192.168.1.1"),
    audit.WithRecordUserAgent("Mozilla/5.0"),
)

// Log access control events
logger.LogAccess(ctx, audit.EventAccessGranted, "user123", "/api/users", audit.ResultSuccess)
Custom Event Types
const (
    EventPasswordChange audit.EventType = "password_change"
    EventAPIKeyCreated  audit.EventType = "api_key_created"
)

record := audit.NewRecord(EventPasswordChange, audit.ResultSuccess).
    WithUserID("user123").
    WithMetadata("changed_by", "admin")
Data Masking
// Automatic masking when logging
config := audit.DefaultConfig()
config.MaskDestination = true // Enabled by default

logger := audit.NewLogger(storage, config)

// Manual masking
masked := audit.MaskEmail("user@example.com")    // u***@example.com
masked = audit.MaskPhone("13800138000")          // 138****8000
masked = audit.MaskIP("192.168.1.100")           // 192.***.100
Log Callback (for Standard Logging)
logger.SetLogCallback(func(record *audit.Record) {
    log.Printf("[AUDIT] %s user=%s result=%s",
        record.EventType, record.UserID, record.Result)
})

Event Types

Built-in Event Types
Category Event Type Description
Challenge challenge_created OTP challenge created
Challenge challenge_verified OTP verification successful
Challenge challenge_revoked Challenge manually revoked
Challenge challenge_expired Challenge expired
Send send_success Message sent successfully
Send send_failed Message send failed
Verification verification_success Verification successful
Verification verification_failed Verification failed
Authentication login_success Login successful
Authentication login_failed Login failed
Authentication logout User logged out
Session session_create Session created
Session session_expire Session expired
Authorization access_granted Access granted
Authorization access_denied Access denied
User user_created User created
User user_updated User updated
User user_deleted User deleted
User user_locked User account locked
User user_unlocked User account unlocked
Rate Limit rate_limited Rate limit triggered
Custom custom Custom event

Configuration

config := &audit.Config{
    Enabled:         true,                    // Enable/disable logging
    MaskDestination: true,                    // Mask phone/email in logs
    TTL:             7 * 24 * time.Hour,      // TTL for Redis storage
    Writer: &audit.WriterConfig{
        QueueSize:   1000,                    // Async queue size
        Workers:     2,                       // Number of workers
        StopTimeout: 10 * time.Second,        // Graceful shutdown timeout
    },
}

Project Structure

audit-kit/
├── types.go           # Record types and event definitions
├── storage.go         # Storage interface and query filter
├── logger.go          # Logger with async support
├── writer.go          # Async writer with worker pool
├── file.go            # File storage (JSON Lines)
├── database.go        # Database storage (PostgreSQL/MySQL/SQLite)
├── redis.go           # Redis storage
├── factory.go         # Storage factory and multi-storage
├── mask.go            # Data masking utilities
└── *_test.go          # Comprehensive tests

Integration Examples

Herald (OTP Service)
package main

import (
    "context"
    audit "github.com/soulteary/audit-kit"
)

func main() {
    storage, _ := audit.NewFileStorage("/var/log/herald-audit.log")
    logger := audit.NewLoggerWithWriter(storage, nil)
    defer logger.Stop()

    // Log OTP challenge created
    logger.LogChallenge(ctx, audit.EventChallengeCreated, challengeID, userID, audit.ResultSuccess,
        audit.WithRecordChannel("sms"),
        audit.WithRecordDestination(phone),
        audit.WithRecordIP(clientIP),
        audit.WithRecordProvider("aliyun", messageID),
    )

    // Log verification result
    logger.LogChallenge(ctx, audit.EventChallengeVerified, challengeID, userID, audit.ResultSuccess)
}
Stargate (Auth Gateway)
package main

import (
    audit "github.com/soulteary/audit-kit"
)

func main() {
    storage, _ := audit.NewDatabaseStorage("postgres://...")
    logger := audit.NewLoggerWithWriter(storage, nil)
    defer logger.Stop()

    // Log successful login
    logger.LogAuth(ctx, audit.EventLoginSuccess, userID, audit.ResultSuccess,
        audit.WithRecordIP(clientIP),
        audit.WithRecordUserAgent(userAgent),
        audit.WithRecordMetadata("auth_method", "otp"),
    )

    // Log access control
    logger.LogAccess(ctx, audit.EventAccessGranted, userID, requestPath, audit.ResultSuccess)
}
Warden (User Service)
package main

import (
    audit "github.com/soulteary/audit-kit"
)

func main() {
    storage := audit.NewRedisStorage(redisClient)
    logger := audit.NewLogger(storage, nil)
    defer logger.Stop()

    // Log user lookup
    logger.Log(ctx, audit.NewRecord(audit.EventCustom, audit.ResultSuccess).
        WithUserID(userID).
        WithMetadata("action", "user_lookup").
        WithMetadata("query_type", "email"),
    )
}

Security and operational notes

  • File storage: Pass only trusted paths to NewFileStorage; do not use user-controlled paths (path traversal or symlinks could write logs elsewhere).
  • Database errors: When logging errors from database storage, avoid logging error.Error() verbatim—drivers may include DSN or passwords. Use fixed messages or error type checks instead.
  • Async queue full: When the writer queue is full, records are dropped (non-blocking). Use OnEnqueueFailed to alert or write to a fallback; size the queue appropriately for your load.
  • Redis: Prefer setting EventID or ChallengeID on records so keys are unique. The index key has no TTL; call Cleanup() periodically or run a job to remove expired key references from the index.
  • Metadata: After JSON round-trip, numeric metadata values become float64; document this if your code type-asserts metadata.

Requirements

  • Go 1.26 or later
  • Optional: github.com/redis/go-redis/v9 (for Redis storage)
  • Optional: github.com/go-sql-driver/mysql or github.com/lib/pq (for database storage)

Test Coverage

Run tests:

go test ./... -v

# With coverage
go test ./... -coverprofile=coverage.out -covermode=atomic
go tool cover -html=coverage.out -o coverage.html
go tool cover -func=coverage.out

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

See LICENSE file for details.

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

View Source
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

func MaskDestination(dest string, channel string) string

MaskDestination masks a destination (phone or email) based on channel

func MaskEmail

func MaskEmail(email string) string

MaskEmail masks an email address

func MaskIP

func MaskIP(ip string) string

MaskIP masks an IP address (keeps first and last octet)

func MaskPhone

func MaskPhone(phone string) string

MaskPhone masks a phone number

func MaskString

func MaskString(s string, keepChars int) string

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
}

Config holds configuration for the audit logger

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns default audit configuration

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

func (*DatabaseStorage) Write

func (s *DatabaseStorage) Write(ctx context.Context, record *Record) error

Write writes an audit record to 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

func (*FileStorage) Write

func (s *FileStorage) Write(ctx context.Context, record *Record) error

Write writes an audit record to the file (JSON Lines format)

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger handles audit logging with optional async writing

func NewLogger

func NewLogger(storage Storage, config *Config) *Logger

NewLogger creates a new audit logger with storage

func NewLoggerWithWriter

func NewLoggerWithWriter(storage Storage, config *Config) *Logger

NewLoggerWithWriter creates a new audit logger with async writer

func (*Logger) GetStats

func (l *Logger) GetStats() *Stats

GetStats returns writer statistics (if async writer is used)

func (*Logger) Log

func (l *Logger) Log(ctx context.Context, record *Record)

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

func (l *Logger) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)

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

func (l *Logger) SetLogCallback(fn func(record *Record))

SetLogCallback sets a callback that is called for each log entry Useful for also logging to standard logger

func (*Logger) Stop

func (l *Logger) Stop() error

Stop stops the logger and releases resources

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) Close

func (m *MultiStorage) Close() error

Close closes all storage 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

func (*MultiStorage) Write

func (m *MultiStorage) Write(ctx context.Context, record *Record) error

Write writes to all storage backends

type NoopStorage

type NoopStorage struct{}

NoopStorage is a no-op storage that discards all records

func NewNoopStorage

func NewNoopStorage() *NoopStorage

NewNoopStorage creates a new no-op storage

func (*NoopStorage) Close

func (s *NoopStorage) Close() error

Close does nothing

func (*NoopStorage) Query

func (s *NoopStorage) Query(ctx context.Context, filter *QueryFilter) ([]*Record, error)

Query returns empty results

func (*NoopStorage) Write

func (s *NoopStorage) Write(ctx context.Context, record *Record) error

Write does nothing

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 NewRecord

func NewRecord(eventType EventType, result Result) *Record

NewRecord creates a new audit record with required fields

func RecordFromJSON

func RecordFromJSON(data []byte) (*Record, error)

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

func (r *Record) Copy() *Record

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

func (r *Record) SetTimestamp(ts int64) *Record

SetTimestamp sets the timestamp (useful for testing)

func (*Record) ToJSON

func (r *Record) ToJSON() ([]byte, error)

ToJSON serializes the record to JSON

func (*Record) WithChallengeID

func (r *Record) WithChallengeID(challengeID string) *Record

WithChallengeID sets the challenge ID

func (*Record) WithChannel

func (r *Record) WithChannel(channel string) *Record

WithChannel sets the channel (sms, email, etc.)

func (*Record) WithDestination

func (r *Record) WithDestination(destination string) *Record

WithDestination sets the destination (phone, email, etc.)

func (*Record) WithDuration

func (r *Record) WithDuration(durationMS int64) *Record

WithDuration sets the operation duration in milliseconds

func (*Record) WithIP

func (r *Record) WithIP(ip string) *Record

WithIP sets the client IP

func (*Record) WithMetadata

func (r *Record) WithMetadata(key string, value interface{}) *Record

WithMetadata sets custom metadata

func (*Record) WithProvider

func (r *Record) WithProvider(provider, messageID string) *Record

WithProvider sets the provider info

func (*Record) WithPurpose

func (r *Record) WithPurpose(purpose string) *Record

WithPurpose sets the purpose (login, reset, etc.)

func (*Record) WithReason

func (r *Record) WithReason(reason string) *Record

WithReason sets the failure reason

func (*Record) WithRequestID

func (r *Record) WithRequestID(requestID string) *Record

WithRequestID sets the request ID

func (*Record) WithResource

func (r *Record) WithResource(resource string) *Record

WithResource sets the accessed resource

func (*Record) WithSessionID

func (r *Record) WithSessionID(sessionID string) *Record

WithSessionID sets the session ID

func (*Record) WithTraceID

func (r *Record) WithTraceID(traceID string) *Record

WithTraceID sets the trace ID

func (*Record) WithUserAgent

func (r *Record) WithUserAgent(ua string) *Record

WithUserAgent sets the user agent

func (*Record) WithUserID

func (r *Record) WithUserID(userID string) *Record

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) Close

func (s *RedisStorage) Close() error

Close closes the Redis connection

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

func (*RedisStorage) TTL

func (s *RedisStorage) TTL() time.Duration

TTL returns the TTL

func (*RedisStorage) Write

func (s *RedisStorage) Write(ctx context.Context, record *Record) error

Write writes an audit record to Redis

type Result

type Result string

Result represents the outcome of an audit event

const (
	ResultSuccess Result = "success"
	ResultFailure Result = "failure"
	ResultPending Result = "pending"
)

type Stats

type Stats struct {
	QueueLength int
	QueueCap    int
	Workers     int
	Started     bool
	Stopped     bool
}

Stats returns writer statistics

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

func (w *Writer) Enqueue(record *Record) bool

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) GetStats

func (w *Writer) GetStats() Stats

GetStats returns current writer statistics

func (*Writer) OnEnqueueFailed

func (w *Writer) OnEnqueueFailed(fn func(record *Record)) *Writer

OnEnqueueFailed sets a callback for when enqueue fails (queue full)

func (*Writer) OnWriteFailed

func (w *Writer) OnWriteFailed(fn func(record *Record, err error)) *Writer

OnWriteFailed sets a callback for when write fails

func (*Writer) Start

func (w *Writer) Start()

Start starts the writer workers

func (*Writer) Stop

func (w *Writer) Stop() error

Stop stops the writer workers gracefully

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL