package logging import ( "strings" "github.com/orchard9/rdev/internal/envutil" ) // Config holds logging configuration. type Config struct { // Level is the default log level. Level Level // Format is the output format (json or text). Format Format // ComponentLevels overrides log levels per component. // Example: {"handlers": LevelDebug, "worker": LevelWarn} ComponentLevels map[string]Level // RedactEnabled enables automatic redaction of sensitive data. RedactEnabled bool // AddSource adds source file/line to log entries (performance impact). AddSource bool } // DefaultConfig returns sensible defaults for production. func DefaultConfig() Config { return Config{ Level: LevelInfo, Format: FormatJSON, ComponentLevels: make(map[string]Level), RedactEnabled: true, AddSource: false, } } // ConfigFromEnv creates a Config from environment variables. // // Environment variables: // - LOG_LEVEL: default level (debug, info, warn, error) // - LOG_FORMAT: output format (json, text) // - LOG_LEVELS: per-component levels (handlers:debug,worker:warn) // - LOG_REDACT: enable/disable sensitive data redaction (true/false) // - LOG_SOURCE: add source file/line to logs (true/false) func ConfigFromEnv() Config { cfg := DefaultConfig() cfg.Level = ParseLevel(envutil.GetEnv("LOG_LEVEL", "info")) cfg.Format = ParseFormat(envutil.GetEnv("LOG_FORMAT", "json")) cfg.RedactEnabled = envutil.GetEnvBool("LOG_REDACT", true) cfg.AddSource = envutil.GetEnvBool("LOG_SOURCE", false) // Parse per-component log levels: "handlers:debug,worker:warn" if levels := envutil.GetEnv("LOG_LEVELS", ""); levels != "" { cfg.ComponentLevels = parseComponentLevels(levels) } return cfg } // parseComponentLevels parses a comma-separated list of component:level pairs. func parseComponentLevels(s string) map[string]Level { result := make(map[string]Level) for pair := range strings.SplitSeq(s, ",") { pair = strings.TrimSpace(pair) if pair == "" { continue } parts := strings.SplitN(pair, ":", 2) if len(parts) != 2 { continue } component := strings.TrimSpace(parts[0]) level := ParseLevel(parts[1]) if component != "" { result[component] = level } } return result } // LevelFor returns the log level for a component, falling back to the default. func (c Config) LevelFor(component string) Level { if level, ok := c.ComponentLevels[component]; ok { return level } return c.Level }