README
ΒΆ
Geekxflood Common
A collection of Go packages shared across GeekxFlood projects.
Current packages:
- Logging: Structured logging built on the standard log/slog
- Config: CUE-powered configuration loading, validation, and hot reload
- Ruler: High-performance rule evaluation engine with structured input/output specifications
- Helpers: Utilities package (foundation for future helpers)
Install packages:
go get github.com/geekxflood/common/logging
go get github.com/geekxflood/common/config
go get github.com/geekxflood/common/ruler
Quality gates:
# Lint
golangci-lint run --config .golangci.yml
# Security scan
gosec -conf=.gosec.json -fmt=json ./...
# Tests
go test ./...
Logging
A comprehensive structured logging package for Go applications built on the standard log/slog
package. This library provides enterprise-grade logging capabilities with a focus on performance, flexibility, and ease of use.
Features
- ποΈ Structured Logging: Built on Go's standard
log/slog
for consistent, structured output - π Multiple Formats: Support for both human-readable logfmt and machine-readable JSON formats
- π§ Component-Aware: Automatic component identification and context extraction for modular applications
- β‘ Dynamic Configuration: Runtime log level changes and configuration updates without restart
- π Flexible Output: Console (stdout/stderr) and file output with automatic directory creation
- π Context Integration: Automatic extraction of request IDs, trace IDs, user IDs, and other context fields
- π§ͺ Comprehensive Testing: Extensive test coverage with 60%+ code coverage
- π Interface-Based: Clean Logger interface for dependency injection and testing
- π Factory Pattern: Component logger factory for consistent logger creation
- π Full Documentation: Complete godoc documentation following Go best practices
Quick Start
Basic Usage
package main
import (
"context"
"github.com/geekxflood/common/logging"
)
func main() {
// Initialize with defaults (info level, logfmt format, stdout)
if err := logging.InitWithDefaults(); err != nil {
panic(err)
}
defer logging.Shutdown() // Always cleanup resources
// Basic structured logging
logging.Info("application started", "version", "1.0.0", "environment", "production")
logging.Warn("configuration missing", "key", "database.timeout", "using_default", true)
logging.Error("connection failed", "error", "connection refused", "retry_count", 3)
}
Custom Configuration
package main
import "github.com/geekxflood/common/logging"
func main() {
// Configure with custom settings
cfg := logging.Config{
Level: "debug", // Enable debug messages
Format: "json", // Machine-readable format
Output: "/var/log/app.log", // File output
AddSource: true, // Include source file info
}
if err := logging.Init(cfg); err != nil {
panic(err)
}
defer logging.Shutdown()
logging.Debug("debug information", "module", "startup")
logging.Info("service ready", "port", 8080)
}
Component-Aware Logging
package main
import "github.com/geekxflood/common/logging"
func main() {
logging.InitWithDefaults()
defer logging.Shutdown()
// Create component-specific loggers
authLogger := logging.NewComponentLogger("auth", "service")
dbLogger := logging.NewComponentLogger("database", "connection")
// Component context is automatically included
authLogger.Info("user authenticated", "user_id", "123", "method", "oauth")
// Output: ... component=auth component_type=service user_id=123 method=oauth
dbLogger.Warn("connection pool low", "active", 8, "max", 10)
// Output: ... component=database component_type=connection active=8 max=10
// Or use component functions directly
logging.InfoComponent("cache", "redis", "cache hit", "key", "user:123", "ttl", 3600)
}
Context-Aware Logging
package main
import (
"context"
"github.com/geekxflood/common/logging"
)
func processRequest(ctx context.Context) {
// Context fields are automatically extracted and included
logging.InfoContext(ctx, "processing request", "action", "get_user")
// Output includes: request_id=req-123 user_id=user-456 trace_id=trace-abc
// Component logging with context
authLogger := logging.NewComponentLogger("auth", "middleware")
authLogger.InfoContext(ctx, "token validated", "token_type", "bearer")
}
func main() {
logging.InitWithDefaults()
defer logging.Shutdown()
// Create context with tracking fields
ctx := context.Background()
ctx = context.WithValue(ctx, "request_id", "req-123")
ctx = context.WithValue(ctx, "user_id", "user-456")
ctx = context.WithValue(ctx, "trace_id", "trace-abc")
processRequest(ctx)
}
Advanced Usage
Dynamic Level Changes
// Change log level at runtime
if err := logging.SetLevel("debug"); err != nil {
logging.Error("failed to set log level", "error", err)
}
// Enable debug logging temporarily
logging.SetLevel("debug")
logging.Debug("detailed debugging info", "state", "processing")
// Reduce verbosity for production
logging.SetLevel("warn") // Only warnings and errors
Logger Interface for Dependency Injection
type UserService struct {
logger logging.Logger
}
func NewUserService(logger logging.Logger) *UserService {
return &UserService{
logger: logger.With("service", "user"),
}
}
func (s *UserService) CreateUser(ctx context.Context, user User) error {
s.logger.InfoContext(ctx, "creating user", "username", user.Username)
if err := s.validateUser(user); err != nil {
s.logger.ErrorContext(ctx, "user validation failed", "error", err)
return err
}
s.logger.InfoContext(ctx, "user created successfully", "user_id", user.ID)
return nil
}
// Usage
func main() {
logging.InitWithDefaults()
defer logging.Shutdown()
logger := logging.GetLogger()
userService := NewUserService(logger)
ctx := context.WithValue(context.Background(), "request_id", "req-789")
userService.CreateUser(ctx, user)
}
Independent Logger Instances
// Create independent loggers for different purposes
auditLogger, auditCloser, err := logging.NewLogger(logging.Config{
Level: "info",
Format: "json",
Output: "/var/log/audit.log",
})
if err != nil {
panic(err)
}
defer auditCloser.Close()
debugLogger, debugCloser, err := logging.NewLogger(logging.Config{
Level: "debug",
Format: "logfmt",
Output: "/tmp/debug.log",
AddSource: true,
})
if err != nil {
panic(err)
}
defer debugCloser.Close()
// Use different loggers for different purposes
auditLogger.Info("user action", "action", "login", "user_id", "123")
debugLogger.Debug("internal state", "cache_size", 1024, "memory_usage", "45MB")
Configuration
The Config
struct provides comprehensive configuration options:
Field | Type | Default | Description |
---|---|---|---|
Level |
string | "info" |
Log level: debug , info , warn , error |
Format |
string | "logfmt" |
Output format: logfmt , json |
Output |
string | "stdout" |
Output destination: stdout , stderr , or file path |
AddSource |
bool | false |
Include source file and line information |
Configuration Examples
// Development configuration
devConfig := logging.Config{
Level: "debug",
Format: "logfmt",
Output: "stdout",
AddSource: true,
}
// Production configuration
prodConfig := logging.Config{
Level: "info",
Format: "json",
Output: "/var/log/app.log",
}
// High-performance configuration
perfConfig := logging.Config{
Level: "warn",
Format: "json",
Output: "stdout",
AddSource: false,
}
Context Fields
The logging package automatically extracts and includes the following context fields when using context-aware logging methods:
request_id
- HTTP request identifieruser_id
- Authenticated user identifiertrace_id
- Distributed tracing identifierspan_id
- Tracing span identifieralert_id
- Alert or incident identifieroperation
- Current operation name
To use context fields, add them to your context:
ctx := context.Background()
ctx = context.WithValue(ctx, "request_id", "req-12345")
ctx = context.WithValue(ctx, "user_id", "user-67890")
logging.InfoContext(ctx, "processing request")
// Output: ... request_id=req-12345 user_id=user-67890
API Reference
Package Functions
Init(config Config) error
- Initialize global loggerInitWithDefaults() error
- Initialize with default settingsShutdown() error
- Cleanup resourcesSetLevel(level string) error
- Change log level at runtimeGet() *slog.Logger
- Get underlying slog loggerGetLogger() Logger
- Get Logger interface
Logging Functions
Debug/Info/Warn/Error(msg string, args ...any)
- Basic loggingDebugContext/InfoContext/WarnContext/ErrorContext(ctx context.Context, msg string, args ...any)
- Context-aware loggingDebugComponent/InfoComponent/WarnComponent/ErrorComponent(component, componentType, msg string, args ...any)
- Component logging*ComponentContext(ctx context.Context, component, componentType, msg string, args ...any)
- Component + context logging
Types
Config
- Logger configurationLogger
- Logging interfaceComponentLogger
- Component-aware logger
Testing
Run the comprehensive test suite:
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests with verbose output
go test -v ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Test Coverage
The package maintains high test coverage with comprehensive tests for:
- β Configuration validation and parsing
- β Multiple output formats (logfmt, JSON)
- β File and console output
- β Component-aware logging
- β Context field extraction
- β Dynamic level changes
- β Logger interface implementation
- β Error handling and edge cases
Performance
The logging package is built on Go's standard log/slog
, providing excellent performance characteristics:
- Zero-allocation logging for most common cases
- Structured logging without reflection overhead
- Lazy evaluation of expensive operations
- Efficient JSON encoding using standard library
- Minimal memory footprint with reusable buffers
Best Practices
- Always call Shutdown(): Use
defer logging.Shutdown()
after initialization - Use structured logging: Prefer key-value pairs over formatted strings
- Choose appropriate levels: Debug for development, Info for operations, Warn for issues, Error for failures
- Include context: Use context-aware methods for request tracing
- Component identification: Use component loggers for modular applications
- Avoid source info in production: Set
AddSource: false
for better performance - Use JSON in production: Machine-readable format for log aggregation
- File rotation: Implement external log rotation for file outputs
Config
A comprehensive configuration management package for Go applications built on CUE (cuelang.org) for schema definition and validation. This library provides enterprise-grade configuration capabilities with a focus on type safety, hot reload, and configuration-driven development.
Features
- π§ CUE-Based Schema Definition: Define configuration schemas using CUE language for powerful validation and type safety
- π Multiple Format Support: Load configuration from YAML and JSON files with automatic validation
- π Hot Reload: Automatic configuration reloading with file watching and change notifications
- β Schema Validation: Comprehensive validation against CUE schemas with detailed error messages
- ποΈ Configuration-Driven Development: Support patterns where application components are dynamically configured
- π Interface-Based Design: Clean interfaces for dependency injection and testing
- π Rich Template Library: Pre-built CUE templates for common configuration patterns (server, database, web app, microservice)
- π§ͺ Comprehensive Testing: Extensive test coverage with 60%+ code coverage and real-world scenarios
- π Full Documentation: Complete godoc documentation following Go best practices
Quick Start
1. Define Schema
Create a CUE schema file (schema.cue
):
package config
server: {
host: string | *"localhost"
port: int & >=1024 & <=65535 | *8080
timeout: string | *"30s"
}
database: {
type: "postgres" | "mysql" | "sqlite" | *"postgres"
host: string | *"localhost"
port: int & >0 | *5432
name: string | *"myapp"
username?: string
password?: string
}
features: {
auth: bool | *false
metrics: bool | *true
debug: bool | *false
}
2. Create Configuration
Create a YAML configuration file (config.yaml
):
server:
host: "0.0.0.0"
port: 8080
timeout: "60s"
database:
type: "postgres"
host: "db.example.com"
port: 5432
name: "production_db"
username: "app_user"
password: "secure_password"
features:
auth: true
metrics: true
debug: false
3. Use in Application
package main
import (
"log"
"github.com/geekxflood/common/config"
)
func main() {
// Create configuration manager
manager, err := config.NewManager(config.Options{
SchemaPath: "schema.cue",
ConfigPath: "config.yaml",
})
if err != nil {
log.Fatal("Failed to create config manager:", err)
}
defer manager.Close()
// Access configuration values
host, _ := manager.GetString("server.host")
port, _ := manager.GetInt("server.port")
timeout, _ := manager.GetDuration("server.timeout")
log.Printf("Server: %s:%d (timeout: %v)", host, port, timeout)
// Check features
if authEnabled, _ := manager.GetBool("features.auth"); authEnabled {
log.Println("Authentication enabled")
}
// Get nested configuration
dbConfig, _ := manager.GetMap("database")
log.Printf("Database: %+v", dbConfig)
}
Advanced Usage
Hot Reload
Enable automatic configuration reloading:
// Enable hot reload
ctx := context.Background()
if err := manager.StartHotReload(ctx); err != nil {
log.Fatal("Failed to start hot reload:", err)
}
// Register change handler
manager.OnConfigChange(func(err error) {
if err != nil {
log.Printf("Config reload failed: %v", err)
} else {
log.Println("Configuration reloaded")
// Reconfigure your application components here
}
})
Configuration-Driven Development
Use configuration to dynamically create application components:
// Get component configurations
components, _ := manager.GetMap("components")
for name, config := range components {
component := factory.Create(name, config)
app.Register(component)
}
Validation
Validate configuration programmatically:
// Validate current configuration
if err := manager.Validate(); err != nil {
log.Printf("Configuration validation failed: %v", err)
}
// Validate a configuration file without loading it
validator, _ := schemaLoader.GetValidator()
if err := validator.ValidateFile("config.yaml"); err != nil {
log.Printf("File validation failed: %v", err)
}
API Reference
Core Types
Manager
Main interface for configuration management:
GetString(path string, defaultValue ...string) (string, error)
GetInt(path string, defaultValue ...int) (int, error)
GetBool(path string, defaultValue ...bool) (bool, error)
GetDuration(path string, defaultValue ...time.Duration) (time.Duration, error)
GetStringSlice(path string, defaultValue ...[]string) ([]string, error)
GetMap(path string) (map[string]any, error)
Exists(path string) bool
Validate() error
StartHotReload(ctx context.Context) error
StopHotReload()
OnConfigChange(callback func(error))
Reload() error
Close() error
Provider
Interface for configuration value access (subset of Manager).
Validator
Interface for configuration validation:
ValidateConfig(config map[string]any) error
ValidateValue(path string, value any) error
ValidateFile(configPath string) error
Functions
NewManager(options Options) (Manager, error)
Creates a new configuration manager with the specified options.
NewSchemaLoader() SchemaLoader
Creates a new CUE schema loader.
Ruler
A high-performance rule evaluation engine with structured input/output specifications for Go applications. Designed for ultra-fast rule evaluation scenarios such as SNMP trap processing, log analysis, and event-driven systems.
Key Features
- High Performance: ~8ΞΌs evaluation times through pre-compilation and smart filtering
- Structured Rules: InputSpec and OutputSpec for self-documenting, type-safe rules
- Smart Rule Selection: Rules pre-filtered based on available input data
- Type Safety: Input and output validation prevents runtime errors
- Thread-Safe: Concurrent evaluation support with optimized locking
- CUE Integration: Leverages CUE's powerful type system and validation
- Fast-Path Optimization: Common patterns bypass full CUE evaluation
- Batch Processing: Efficient evaluation of multiple input sets
Quick Example
package main
import (
"context"
"fmt"
"log"
"github.com/geekxflood/common/ruler"
)
func main() {
// Define structured rule configuration
config := map[string]any{
"config": map[string]any{
"enabled": true,
"default_message": "no rule found",
},
"rules": []any{
map[string]any{
"name": "high_cpu_alert",
"description": "Triggers when CPU usage exceeds threshold",
"inputs": []any{
map[string]any{
"name": "cpu_usage",
"type": "number",
"required": true,
"description": "CPU usage percentage",
},
},
"expr": `cpu_usage > 80`,
"outputs": []any{
map[string]any{
"name": "alert",
"description": "High CPU usage alert",
"fields": map[string]any{
"severity": map[string]any{
"type": "string",
"required": true,
"default": "high",
},
"message": map[string]any{
"type": "string",
"required": true,
"default": "High CPU usage detected",
},
},
},
},
},
},
}
// Create ruler
r, err := ruler.NewRuler(config)
if err != nil {
log.Fatal(err)
}
// Evaluate with structured input data
inputs := ruler.Inputs{
"cpu_usage": 85.5,
}
result, err := r.Evaluate(context.Background(), inputs)
if err != nil {
log.Fatal(err)
}
if result.Output != nil {
fmt.Printf("Rule matched: %s\n", result.MatchedRule.Name)
for outputName, outputData := range result.Output.GeneratedOutputs {
fmt.Printf("Output %s: %+v\n", outputName, outputData)
}
} else {
fmt.Println(result.DefaultMessage)
}
}
Structured Approach Benefits
- Self-Documenting: Rules clearly specify their input requirements and output structure
- Type Safety: Input validation ensures rules only run with compatible data
- Performance: Rules are pre-filtered based on available input data
- Maintainability: Clear structure makes rules easier to understand and modify
- Developer Experience: IDE support and clear error messages
For detailed documentation, see ruler/ruler.md.
Testing
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests with verbose output
go test -v ./...
Best Practices
- Use CUE Defaults: Define sensible defaults in your CUE schemas
- Validate Early: Validate configuration at application startup
- Handle Hot Reload: Design your application to handle configuration changes gracefully
- Use Templates: Leverage the provided templates for common patterns
- Environment-Specific Configs: Use different configuration files for different environments
- Secure Secrets: Don't store secrets in configuration files; use environment variables or secret management systems
Directories
ΒΆ
Path | Synopsis |
---|---|
Package config provides comprehensive configuration management for Go applications using CUE (cuelang.org) for schema definition and validation.
|
Package config provides comprehensive configuration management for Go applications using CUE (cuelang.org) for schema definition and validation. |
Package helpers provides utility functions for common tasks.
|
Package helpers provides utility functions for common tasks. |
Package logging provides structured logging capabilities using Go's standard log/slog package.
|
Package logging provides structured logging capabilities using Go's standard log/slog package. |
Package ruler provides a production-grade library for evaluating CUE expressions against structured input data with intelligent rule selection and type-safe outputs.
|
Package ruler provides a production-grade library for evaluating CUE expressions against structured input data with intelligent rule selection and type-safe outputs. |