Skip to main content
Glama
config.go11.7 kB
package logs import ( "embed" "fmt" "os" "path/filepath" "regexp" "github.com/BurntSushi/toml" ) //go:embed error_parsing.toml var defaultConfigFS embed.FS // ErrorParsingConfig represents the complete TOML configuration structure type ErrorParsingConfig struct { Settings Settings `toml:"settings"` LanguageDetection map[string]LanguageConfig `toml:"language_detection"` ErrorPatterns map[string]map[string]Pattern `toml:"error_patterns"` StackPatterns map[string]StackConfig `toml:"stack_patterns"` ContinuationPatterns ContinuationConfig `toml:"continuation_patterns"` EndPatterns EndConfig `toml:"end_patterns"` LogPrefixes LogPrefixConfig `toml:"log_prefixes"` CustomErrorTypes map[string]CustomErrorType `toml:"custom_error_types"` Frameworks map[string]FrameworkConfig `toml:"frameworks"` Limits Limits `toml:"limits"` } type Settings struct { MaxContextLines int `toml:"max_context_lines"` MaxContextWaitSeconds int `toml:"max_context_wait_seconds"` AutoDetectLanguage bool `toml:"auto_detect_language"` CriticalKeywords []string `toml:"critical_keywords"` ContinuationKeywords []string `toml:"continuation_keywords"` } type LanguageConfig struct { FileExtensions []string `toml:"file_extensions"` StackPatterns []string `toml:"stack_patterns"` FrameworkPatterns []string `toml:"framework_patterns"` ErrorPatterns []string `toml:"error_patterns"` } type Pattern struct { Pattern string `toml:"pattern"` Type string `toml:"type"` Severity string `toml:"severity"` SingleLine bool `toml:"single_line"` Description string `toml:"description"` // Compiled regex (not in TOML) regex *regexp.Regexp } type StackConfig struct { Patterns []string `toml:"patterns"` // Compiled regexes (not in TOML) regexes []*regexp.Regexp } type ContinuationConfig struct { General PatternList `toml:"general"` JavaScript PatternList `toml:"javascript"` Python PatternList `toml:"python"` } type PatternList struct { Patterns []string `toml:"patterns"` // Compiled regexes (not in TOML) regexes []*regexp.Regexp } type EndConfig struct { Patterns []string `toml:"patterns"` // Compiled regexes (not in TOML) regexes []*regexp.Regexp } type LogPrefixConfig struct { Timestamp PatternList `toml:"timestamp"` Process PatternList `toml:"process"` ConditionalProcess ConditionalProcessConfig `toml:"conditional_process"` } type ConditionalProcessConfig struct { Patterns []string `toml:"patterns"` ExcludeIfMatches []string `toml:"exclude_if_matches"` // Compiled regexes (not in TOML) regexes []*regexp.Regexp excludeRegexes []*regexp.Regexp } type CustomErrorType struct { Type string `toml:"type"` Patterns []string `toml:"patterns"` ExtractHostname bool `toml:"extract_hostname"` HostnamePattern string `toml:"hostname_pattern"` DNSErrorReplacement map[string]string `toml:"dns_error_replacement"` // Compiled regexes (not in TOML) regexes []*regexp.Regexp hostnameRegex *regexp.Regexp } type FrameworkConfig struct { HookErrors []string `toml:"hook_errors"` JSXErrors []string `toml:"jsx_errors"` BuildErrorContext int `toml:"build_error_context"` LintIntegration bool `toml:"lint_integration"` TemplateErrorContext int `toml:"template_error_context"` CompositionAPIErrors bool `toml:"composition_api_errors"` Patterns []string `toml:"patterns"` } type Limits struct { MaxErrorsInMemory int `toml:"max_errors_in_memory"` MaxErrorSizeBytes int `toml:"max_error_size_bytes"` ErrorCompletionTimeoutMs int `toml:"error_completion_timeout_ms"` MaxStackTraceLines int `toml:"max_stack_trace_lines"` DebugLogging bool `toml:"debug_logging"` } // LoadConfig loads the error parsing configuration from TOML file func LoadConfig(configPath string) (*ErrorParsingConfig, error) { var config ErrorParsingConfig // Use default config if no path provided or file doesn't exist var configData []byte var err error if configPath == "" || !fileExists(configPath) { // Load embedded default configuration configData, err = defaultConfigFS.ReadFile("error_parsing.toml") if err != nil { return nil, fmt.Errorf("failed to load default config: %w", err) } } else { // Load user-provided configuration configData, err = os.ReadFile(configPath) if err != nil { return nil, fmt.Errorf("failed to read config file %s: %w", configPath, err) } } // Parse TOML if err := toml.Unmarshal(configData, &config); err != nil { return nil, fmt.Errorf("failed to parse TOML config: %w", err) } // Compile all regex patterns if err := compileRegexes(&config); err != nil { return nil, fmt.Errorf("failed to compile regex patterns: %w", err) } // Set defaults if not specified setDefaults(&config) return &config, nil } // LoadDefaultConfig loads the embedded default configuration func LoadDefaultConfig() (*ErrorParsingConfig, error) { return LoadConfig("") } // GetUserConfigPath returns the path where user config should be stored func GetUserConfigPath() string { homeDir, _ := os.UserHomeDir() return filepath.Join(homeDir, ".brummer", "error_parsing.toml") } // CreateUserConfig creates a user configuration file with the default settings func CreateUserConfig() error { configPath := GetUserConfigPath() // Create directory if it doesn't exist if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil { return fmt.Errorf("failed to create config directory: %w", err) } // Read default config configData, err := defaultConfigFS.ReadFile("error_parsing.toml") if err != nil { return fmt.Errorf("failed to read default config: %w", err) } // Write to user config path if err := os.WriteFile(configPath, configData, 0644); err != nil { return fmt.Errorf("failed to write user config: %w", err) } return nil } func compileRegexes(config *ErrorParsingConfig) error { // Compile error patterns for language, patterns := range config.ErrorPatterns { compiledPatterns := make(map[string]Pattern) for name, pattern := range patterns { regex, err := regexp.Compile(pattern.Pattern) if err != nil { return fmt.Errorf("failed to compile error pattern %s.%s: %w", language, name, err) } pattern.regex = regex compiledPatterns[name] = pattern } config.ErrorPatterns[language] = compiledPatterns } // Compile stack patterns for language, stackConfig := range config.StackPatterns { regexes := make([]*regexp.Regexp, len(stackConfig.Patterns)) for i, pattern := range stackConfig.Patterns { regex, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile stack pattern %s[%d]: %w", language, i, err) } regexes[i] = regex } stackConfig.regexes = regexes config.StackPatterns[language] = stackConfig } // Compile continuation patterns if err := compilePatternList(&config.ContinuationPatterns.General); err != nil { return fmt.Errorf("failed to compile general continuation patterns: %w", err) } if err := compilePatternList(&config.ContinuationPatterns.JavaScript); err != nil { return fmt.Errorf("failed to compile javascript continuation patterns: %w", err) } if err := compilePatternList(&config.ContinuationPatterns.Python); err != nil { return fmt.Errorf("failed to compile python continuation patterns: %w", err) } // Compile end patterns regexes := make([]*regexp.Regexp, len(config.EndPatterns.Patterns)) for i, pattern := range config.EndPatterns.Patterns { regex, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile end pattern[%d]: %w", i, err) } regexes[i] = regex } config.EndPatterns.regexes = regexes // Compile log prefix patterns if err := compilePatternList(&config.LogPrefixes.Timestamp); err != nil { return fmt.Errorf("failed to compile timestamp patterns: %w", err) } if err := compilePatternList(&config.LogPrefixes.Process); err != nil { return fmt.Errorf("failed to compile process patterns: %w", err) } // Compile conditional process patterns regexes = make([]*regexp.Regexp, len(config.LogPrefixes.ConditionalProcess.Patterns)) for i, pattern := range config.LogPrefixes.ConditionalProcess.Patterns { regex, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile conditional process pattern[%d]: %w", i, err) } regexes[i] = regex } config.LogPrefixes.ConditionalProcess.regexes = regexes excludeRegexes := make([]*regexp.Regexp, len(config.LogPrefixes.ConditionalProcess.ExcludeIfMatches)) for i, pattern := range config.LogPrefixes.ConditionalProcess.ExcludeIfMatches { regex, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile conditional exclude pattern[%d]: %w", i, err) } excludeRegexes[i] = regex } config.LogPrefixes.ConditionalProcess.excludeRegexes = excludeRegexes // Compile custom error type patterns for name, customType := range config.CustomErrorTypes { regexes := make([]*regexp.Regexp, len(customType.Patterns)) for i, pattern := range customType.Patterns { regex, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile custom error type %s pattern[%d]: %w", name, i, err) } regexes[i] = regex } customType.regexes = regexes if customType.HostnamePattern != "" { hostnameRegex, err := regexp.Compile(customType.HostnamePattern) if err != nil { return fmt.Errorf("failed to compile hostname pattern for %s: %w", name, err) } customType.hostnameRegex = hostnameRegex } config.CustomErrorTypes[name] = customType } return nil } func compilePatternList(patternList *PatternList) error { regexes := make([]*regexp.Regexp, len(patternList.Patterns)) for i, pattern := range patternList.Patterns { regex, err := regexp.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile pattern[%d]: %w", i, err) } regexes[i] = regex } patternList.regexes = regexes return nil } func setDefaults(config *ErrorParsingConfig) { if config.Settings.MaxContextLines == 0 { config.Settings.MaxContextLines = 50 } if config.Settings.MaxContextWaitSeconds == 0 { config.Settings.MaxContextWaitSeconds = 2 } if config.Limits.MaxErrorsInMemory == 0 { config.Limits.MaxErrorsInMemory = 1000 } if config.Limits.MaxErrorSizeBytes == 0 { config.Limits.MaxErrorSizeBytes = 10240 } if config.Limits.ErrorCompletionTimeoutMs == 0 { config.Limits.ErrorCompletionTimeoutMs = 2000 } if config.Limits.MaxStackTraceLines == 0 { config.Limits.MaxStackTraceLines = 50 } } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } // Helper methods for accessing compiled regexes func (p *Pattern) Regex() *regexp.Regexp { return p.regex } func (s *StackConfig) Regexes() []*regexp.Regexp { return s.regexes } func (p *PatternList) Regexes() []*regexp.Regexp { return p.regexes } func (e *EndConfig) Regexes() []*regexp.Regexp { return e.regexes } func (c *ConditionalProcessConfig) Regexes() []*regexp.Regexp { return c.regexes } func (c *ConditionalProcessConfig) ExcludeRegexes() []*regexp.Regexp { return c.excludeRegexes } func (c *CustomErrorType) Regexes() []*regexp.Regexp { return c.regexes } func (c *CustomErrorType) HostnameRegex() *regexp.Regexp { return c.hostnameRegex }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/standardbeagle/brummer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server