feat: implement drain3 based generic log-parser

This commit is contained in:
Patryk Hegenberg 2026-01-18 16:51:44 +01:00
parent 1d1568e3ee
commit 5af49f926a
17 changed files with 612 additions and 220 deletions

View file

@ -1,93 +1,3 @@
// package patterns
// import (
// "fmt"
// "regexp"
// "sync"
// "gopkg.in/yaml.v3"
// "os"
// )
// type PatternConfig struct {
// Patterns map[string]map[string]PatternDefinition `yaml:"patterns"`
// }
// type PatternDefinition struct {
// Regex string `yaml:"regex"`
// Description string `yaml:"description,omitempty"`
// }
// type Repository struct {
// compiledPatterns map[string]map[string]*regexp.Regexp
// mu sync.RWMutex
// }
// var (
// instance *Repository
// once sync.Once
// )
// func GetInstance() *Repository {
// once.Do(func() {
// instance = &Repository{
// compiledPatterns: make(map[string]map[string]*regexp.Regexp),
// }
// })
// return instance
// }
// func (r *Repository) Load(path string) error {
// r.mu.Lock()
// defer r.mu.Unlock()
// data, err := os.ReadFile(path)
// if err != nil {
// return fmt.Errorf("failed to read pattern config: %w", err)
// }
// var config PatternConfig
// if err := yaml.Unmarshal(data, &config); err != nil {
// return fmt.Errorf("failed to parse pattern config: %w", err)
// }
// for service, patterns := range config.Patterns {
// if _, exists := r.compiledPatterns[service]; !exists {
// r.compiledPatterns[service] = make(map[string]*regexp.Regexp)
// }
// for name, def := range patterns {
// compiled, err := regexp.Compile(def.Regex)
// if err != nil {
// return fmt.Errorf("invalid regex for %s/%s: %w", service, name, err)
// }
// r.compiledPatterns[service][name] = compiled
// }
// }
// return nil
// }
// func (r *Repository) Get(service string, name string) (*regexp.Regexp, error) {
// r.mu.RLock()
// defer r.mu.RUnlock()
// if svcPatterns, ok := r.compiledPatterns[service]; ok {
// if pattern, ok := svcPatterns[name]; ok {
// return pattern, nil
// }
// }
// return nil, fmt.Errorf("pattern not found: %s/%s", service, name)
// }
// func (r *Repository) MustGet(service string, name string) *regexp.Regexp {
// p, err := r.Get(service, name)
// if err != nil {
// panic(err)
// }
// return p
// }
package patterns
import (
@ -99,7 +9,6 @@ import (
"gopkg.in/yaml.v3"
)
// Struktur der YAML Datei
type Config struct {
Patterns map[string]ServiceConfig `yaml:"patterns"`
}
@ -111,10 +20,9 @@ type ServiceConfig struct {
type ExtractorConfig struct {
Name string `yaml:"name"`
Regex string `yaml:"regex"`
Fields map[string]string `yaml:"fields"` // Name -> Typ (int, float, string)
Fields map[string]string `yaml:"fields"`
}
// Interne kompilierte Struktur
type CompiledExtractor struct {
Name string
Pattern *regexp.Regexp

View file

@ -0,0 +1,51 @@
package patterns
import (
"os"
"testing"
)
func TestRepository_Load(t *testing.T) {
content := `
patterns:
test_service:
extractors:
- name: "test_pattern"
regex: '^Test (?P<id>\d+) (?P<value>\d+\.\d+)$'
fields:
id: "int"
value: "float"
`
tmpfile, err := os.CreateTemp("", "patterns_test_*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.Write([]byte(content)); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
repo := GetInstance()
err = repo.Load(tmpfile.Name())
if err != nil {
t.Fatalf("Failed to load repository: %v", err)
}
extractors := repo.GetExtractors("test_service")
if len(extractors) != 1 {
t.Errorf("Expected 1 extractor, got %d", len(extractors))
}
ext := extractors[0]
if ext.Name != "test_pattern" {
t.Errorf("Expected name 'test_pattern', got '%s'", ext.Name)
}
if !ext.Pattern.MatchString("Test 123 45.67") {
t.Error("Regex did not match valid string")
}
}