v0.4.5 refactor and cleanup, minor bug fixes, default config update
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// FilterType represents the filter type
|
||||
// Represents the filter type
|
||||
type FilterType string
|
||||
|
||||
const (
|
||||
@@ -14,7 +14,7 @@ const (
|
||||
FilterTypeExclude FilterType = "exclude" // Blacklist - matching logs are dropped
|
||||
)
|
||||
|
||||
// FilterLogic represents how multiple patterns are combined
|
||||
// Represents how multiple patterns are combined
|
||||
type FilterLogic string
|
||||
|
||||
const (
|
||||
@@ -22,7 +22,7 @@ const (
|
||||
FilterLogicAnd FilterLogic = "and" // Match all patterns
|
||||
)
|
||||
|
||||
// FilterConfig represents filter configuration
|
||||
// Represents filter configuration
|
||||
type FilterConfig struct {
|
||||
Type FilterType `toml:"type"`
|
||||
Logic FilterLogic `toml:"logic"`
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RateLimitPolicy defines the action to take when a rate limit is exceeded.
|
||||
// Defines the action to take when a rate limit is exceeded.
|
||||
type RateLimitPolicy int
|
||||
|
||||
const (
|
||||
@@ -16,7 +16,7 @@ const (
|
||||
PolicyDrop
|
||||
)
|
||||
|
||||
// RateLimitConfig defines the configuration for pipeline-level rate limiting.
|
||||
// Defines the configuration for pipeline-level rate limiting.
|
||||
type RateLimitConfig struct {
|
||||
// Rate is the number of log entries allowed per second. Default: 0 (disabled).
|
||||
Rate float64 `toml:"rate"`
|
||||
|
||||
@@ -11,11 +11,6 @@ import (
|
||||
lconfig "github.com/lixenwraith/config"
|
||||
)
|
||||
|
||||
// LoadContext holds all configuration sources
|
||||
type LoadContext struct {
|
||||
FlagConfig any // Parsed command-line flags from main
|
||||
}
|
||||
|
||||
func defaults() *Config {
|
||||
return &Config{
|
||||
// Top-level flag defaults
|
||||
@@ -69,7 +64,7 @@ func defaults() *Config {
|
||||
}
|
||||
}
|
||||
|
||||
// Load is the single entry point for loading all configuration
|
||||
// Single entry point for loading all configuration
|
||||
func Load(args []string) (*Config, error) {
|
||||
configPath, isExplicit := resolveConfigPath(args)
|
||||
// Build configuration with all sources
|
||||
@@ -124,7 +119,7 @@ func Load(args []string) (*Config, error) {
|
||||
return finalConfig, finalConfig.validate()
|
||||
}
|
||||
|
||||
// resolveConfigPath returns the configuration file path
|
||||
// Returns the configuration file path
|
||||
func resolveConfigPath(args []string) (path string, isExplicit bool) {
|
||||
// 1. Check for --config flag in command-line arguments (highest precedence)
|
||||
for i, arg := range args {
|
||||
@@ -167,7 +162,7 @@ func customEnvTransform(path string) string {
|
||||
return env
|
||||
}
|
||||
|
||||
// applyConsoleTargetOverrides centralizes console target configuration
|
||||
// Centralizes console target configuration
|
||||
func applyConsoleTargetOverrides(cfg *Config) error {
|
||||
// Check environment variable for console target override
|
||||
consoleTarget := os.Getenv("LOGWISP_CONSOLE_TARGET")
|
||||
|
||||
@@ -3,7 +3,7 @@ package config
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LogConfig represents logging configuration for LogWisp
|
||||
// Represents logging configuration for LogWisp
|
||||
type LogConfig struct {
|
||||
// Output mode: "file", "stdout", "stderr", "both", "none"
|
||||
Output string `toml:"output"`
|
||||
@@ -44,10 +44,10 @@ type LogConsoleConfig struct {
|
||||
Format string `toml:"format"`
|
||||
}
|
||||
|
||||
// DefaultLogConfig returns sensible logging defaults
|
||||
// Returns sensible logging defaults
|
||||
func DefaultLogConfig() *LogConfig {
|
||||
return &LogConfig{
|
||||
Output: "stderr",
|
||||
Output: "stdout",
|
||||
Level: "info",
|
||||
File: &LogFileConfig{
|
||||
Directory: "./log",
|
||||
@@ -57,7 +57,7 @@ func DefaultLogConfig() *LogConfig {
|
||||
RetentionHours: 168, // 7 days
|
||||
},
|
||||
Console: &LogConsoleConfig{
|
||||
Target: "stderr",
|
||||
Target: "stdout",
|
||||
Format: "txt",
|
||||
},
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func DefaultLogConfig() *LogConfig {
|
||||
func validateLogConfig(cfg *LogConfig) error {
|
||||
validOutputs := map[string]bool{
|
||||
"file": true, "stdout": true, "stderr": true,
|
||||
"both": true, "none": true,
|
||||
"both": true, "all": true, "none": true,
|
||||
}
|
||||
if !validOutputs[cfg.Output] {
|
||||
return fmt.Errorf("invalid log output mode: %s", cfg.Output)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PipelineConfig represents a data processing pipeline
|
||||
// Represents a data processing pipeline
|
||||
type PipelineConfig struct {
|
||||
// Pipeline identifier (used in logs and metrics)
|
||||
Name string `toml:"name"`
|
||||
@@ -34,16 +34,16 @@ type PipelineConfig struct {
|
||||
Auth *AuthConfig `toml:"auth"`
|
||||
}
|
||||
|
||||
// SourceConfig represents an input data source
|
||||
// Represents an input data source
|
||||
type SourceConfig struct {
|
||||
// Source type: "directory", "file", "stdin", etc.
|
||||
// Source type: "directory", "stdin", "tcp", "http"
|
||||
Type string `toml:"type"`
|
||||
|
||||
// Type-specific configuration options
|
||||
Options map[string]any `toml:"options"`
|
||||
}
|
||||
|
||||
// SinkConfig represents an output destination
|
||||
// Represents an output destination
|
||||
type SinkConfig struct {
|
||||
// Sink type: "http", "tcp", "file", "stdout", "stderr"
|
||||
Type string `toml:"type"`
|
||||
@@ -59,7 +59,7 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
|
||||
switch cfg.Type {
|
||||
case "directory":
|
||||
// Validate directory source options
|
||||
// Validate path
|
||||
path, ok := cfg.Options["path"].(string)
|
||||
if !ok || path == "" {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: directory source requires 'path' option",
|
||||
@@ -72,7 +72,7 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
pipelineName, sourceIndex)
|
||||
}
|
||||
|
||||
// Validate pattern if provided
|
||||
// Validate pattern
|
||||
if pattern, ok := cfg.Options["pattern"].(string); ok && pattern != "" {
|
||||
// Try to compile as glob pattern (will be converted to regex internally)
|
||||
if strings.Count(pattern, "*") == 0 && strings.Count(pattern, "?") == 0 {
|
||||
@@ -84,7 +84,7 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate check interval if provided
|
||||
// Validate check interval
|
||||
if interval, ok := cfg.Options["check_interval_ms"]; ok {
|
||||
if intVal, ok := interval.(int64); ok {
|
||||
if intVal < 10 {
|
||||
@@ -98,17 +98,16 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
}
|
||||
|
||||
case "stdin":
|
||||
// No specific validation needed for stdin
|
||||
|
||||
case "http":
|
||||
// Validate HTTP source options
|
||||
port, ok := cfg.Options["port"].(int64)
|
||||
if !ok || port < 1 || port > 65535 {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: invalid or missing HTTP port",
|
||||
pipelineName, sourceIndex)
|
||||
// Validate buffer size
|
||||
if bufSize, ok := cfg.Options["buffer_size"].(int64); ok {
|
||||
if bufSize < 1 {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: stdin buffer_size must be positive: %d",
|
||||
pipelineName, sourceIndex, bufSize)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate host if provided
|
||||
case "http":
|
||||
// Validate host
|
||||
if host, ok := cfg.Options["host"].(string); ok && host != "" {
|
||||
if net.ParseIP(host) == nil {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: invalid IP address: %s",
|
||||
@@ -116,22 +115,29 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate path if provided
|
||||
if ingestPath, ok := cfg.Options["ingest_path"].(string); ok {
|
||||
if !strings.HasPrefix(ingestPath, "/") {
|
||||
// Validate port
|
||||
port, ok := cfg.Options["port"].(int64)
|
||||
if !ok || port < 1 || port > 65535 {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: invalid or missing HTTP port",
|
||||
pipelineName, sourceIndex)
|
||||
}
|
||||
|
||||
// Validate path
|
||||
if path, ok := cfg.Options["ingest_path"].(string); ok {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: ingest path must start with /: %s",
|
||||
pipelineName, sourceIndex, ingestPath)
|
||||
pipelineName, sourceIndex, path)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate net_limit if present within Options
|
||||
// Validate net_limit
|
||||
if rl, ok := cfg.Options["net_limit"].(map[string]any); ok {
|
||||
if err := validateNetLimitOptions("HTTP source", pipelineName, sourceIndex, rl); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate TLS if present
|
||||
// Validate TLS
|
||||
if tls, ok := cfg.Options["tls"].(map[string]any); ok {
|
||||
if err := validateTLSOptions("HTTP source", pipelineName, sourceIndex, tls); err != nil {
|
||||
return err
|
||||
@@ -139,14 +145,7 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
}
|
||||
|
||||
case "tcp":
|
||||
// Validate TCP source options
|
||||
port, ok := cfg.Options["port"].(int64)
|
||||
if !ok || port < 1 || port > 65535 {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: invalid or missing TCP port",
|
||||
pipelineName, sourceIndex)
|
||||
}
|
||||
|
||||
// Validate host if provided
|
||||
// Validate host
|
||||
if host, ok := cfg.Options["host"].(string); ok && host != "" {
|
||||
if net.ParseIP(host) == nil {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: invalid IP address: %s",
|
||||
@@ -154,14 +153,21 @@ func validateSource(pipelineName string, sourceIndex int, cfg *SourceConfig) err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate net_limit if present within Options
|
||||
// Validate port
|
||||
port, ok := cfg.Options["port"].(int64)
|
||||
if !ok || port < 1 || port > 65535 {
|
||||
return fmt.Errorf("pipeline '%s' source[%d]: invalid or missing TCP port",
|
||||
pipelineName, sourceIndex)
|
||||
}
|
||||
|
||||
// Validate net_limit
|
||||
if rl, ok := cfg.Options["net_limit"].(map[string]any); ok {
|
||||
if err := validateNetLimitOptions("TCP source", pipelineName, sourceIndex, rl); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate TLS if present
|
||||
// Validate TLS
|
||||
if tls, ok := cfg.Options["tls"].(map[string]any); ok {
|
||||
if err := validateTLSOptions("TCP source", pipelineName, sourceIndex, tls); err != nil {
|
||||
return err
|
||||
@@ -337,7 +343,7 @@ func validateSink(pipelineName string, sinkIndex int, cfg *SinkConfig, allPorts
|
||||
}
|
||||
|
||||
case "tcp_client":
|
||||
// FIXED: Added validation for TCP client sink
|
||||
// Added validation for TCP client sink
|
||||
// Validate address
|
||||
address, ok := cfg.Options["address"].(string)
|
||||
if !ok || address == "" {
|
||||
@@ -368,20 +374,21 @@ func validateSink(pipelineName string, sinkIndex int, cfg *SinkConfig, allPorts
|
||||
}
|
||||
|
||||
case "file":
|
||||
// Validate file sink options
|
||||
// Validate directory
|
||||
directory, ok := cfg.Options["directory"].(string)
|
||||
if !ok || directory == "" {
|
||||
return fmt.Errorf("pipeline '%s' sink[%d]: file sink requires 'directory' option",
|
||||
pipelineName, sinkIndex)
|
||||
}
|
||||
|
||||
// Validate filename
|
||||
name, ok := cfg.Options["name"].(string)
|
||||
if !ok || name == "" {
|
||||
return fmt.Errorf("pipeline '%s' sink[%d]: file sink requires 'name' option",
|
||||
pipelineName, sinkIndex)
|
||||
}
|
||||
|
||||
// Validate numeric options
|
||||
// Validate size options
|
||||
if maxSize, ok := cfg.Options["max_size_mb"].(int64); ok {
|
||||
if maxSize < 1 {
|
||||
return fmt.Errorf("pipeline '%s' sink[%d]: max_size_mb must be positive: %d",
|
||||
@@ -396,6 +403,14 @@ func validateSink(pipelineName string, sinkIndex int, cfg *SinkConfig, allPorts
|
||||
}
|
||||
}
|
||||
|
||||
if minDiskFree, ok := cfg.Options["min_disk_free_mb"].(int64); ok {
|
||||
if minDiskFree < 0 {
|
||||
return fmt.Errorf("pipeline '%s' sink[%d]: min_disk_free_mb cannot be negative: %d",
|
||||
pipelineName, sinkIndex, minDiskFree)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate retention period
|
||||
if retention, ok := cfg.Options["retention_hours"].(float64); ok {
|
||||
if retention < 0 {
|
||||
return fmt.Errorf("pipeline '%s' sink[%d]: retention_hours cannot be negative: %f",
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
lconfig "github.com/lixenwraith/config"
|
||||
)
|
||||
|
||||
// SaveToFile saves the configuration to the specified file path.
|
||||
// It uses the lconfig library's atomic file saving capabilities.
|
||||
// Saves the configuration to the specified file path.
|
||||
func (c *Config) SaveToFile(path string) error {
|
||||
if path == "" {
|
||||
return fmt.Errorf("cannot save config: path is empty")
|
||||
|
||||
@@ -170,7 +170,7 @@ func validateNetLimitOptions(serverType, pipelineName string, sinkIndex int, rl
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIPv4Entry ensures an IP or CIDR is IPv4
|
||||
// Ensures an IP or CIDR is IPv4
|
||||
func validateIPv4Entry(entry string) error {
|
||||
// Handle single IP
|
||||
if !strings.Contains(entry, "/") {
|
||||
|
||||
Reference in New Issue
Block a user