1
0
mirror of https://github.com/amir20/dozzle.git synced 2025-12-21 13:23:07 +01:00
Files
dozzle/internal/support/web/regex.go
2025-05-21 08:52:26 -07:00

199 lines
4.7 KiB
Go

package support_web
import (
"fmt"
"regexp"
"strings"
"github.com/amir20/dozzle/internal/container"
"github.com/rs/zerolog/log"
orderedmap "github.com/wk8/go-ordered-map/v2"
)
// PatternMatcher defines the interface for a regex pattern matcher
type PatternMatcher struct {
Regex *regexp.Regexp
MarkerStart string
MarkerEnd string
}
// NewPatternMatcher creates a new pattern matcher with the specified regex and markers
func NewPatternMatcher(re *regexp.Regexp, markerStart, markerEnd string) *PatternMatcher {
return &PatternMatcher{
Regex: re,
MarkerStart: markerStart,
MarkerEnd: markerEnd,
}
}
// CreateRegex compiles a regex pattern with optional case-insensitive flag
func CreateRegex(pattern string, caseInsensitive bool) (*regexp.Regexp, error) {
flags := ""
if caseInsensitive || pattern == strings.ToLower(pattern) {
flags = "(?i)"
}
re, err := regexp.Compile(flags + pattern)
if err != nil {
log.Debug().Err(err).Str("pattern", pattern).Msg("failed to compile regex")
return nil, err
}
return re, nil
}
// MarkInLogEvent applies the pattern matcher to the log event's message
func (pm *PatternMatcher) MarkInLogEvent(logEvent *container.LogEvent) bool {
switch value := logEvent.Message.(type) {
case string:
if pm.Regex.MatchString(value) {
logEvent.Message = pm.Regex.ReplaceAllString(value, pm.MarkerStart+"$0"+pm.MarkerEnd)
return true
}
case *orderedmap.OrderedMap[string, any]:
return pm.markMapAny(value)
case *orderedmap.OrderedMap[string, string]:
return pm.markMapString(value)
case map[string]interface{}:
return pm.markMap(value)
case map[string]string:
panic("not implemented")
default:
log.Debug().Type("type", value).Msg("unknown logEvent type")
}
return false
}
func (pm *PatternMatcher) markMapAny(orderedMap *orderedmap.OrderedMap[string, any]) bool {
found := false
for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() {
switch value := pair.Value.(type) {
case string:
if replaced, matched := pm.markString(value); matched {
found = true
orderedMap.Set(pair.Key, replaced)
}
case []any:
if pm.markArray(value) {
found = true
}
case *orderedmap.OrderedMap[string, any]:
if pm.markMapAny(value) {
found = true
}
case *orderedmap.OrderedMap[string, string]:
if pm.markMapString(value) {
found = true
}
case map[string]interface{}:
if pm.markMap(value) {
found = true
}
case int, float64, bool:
formatted := fmt.Sprintf("%v", value)
if pm.Regex.MatchString(formatted) {
orderedMap.Set(pair.Key, pm.Regex.ReplaceAllString(formatted, pm.MarkerStart+"$0"+pm.MarkerEnd))
found = true
}
default:
log.Debug().Type("type", value).Msg("unknown logEvent type inside markMapAny")
}
}
return found
}
func (pm *PatternMatcher) markMap(data map[string]interface{}) bool {
found := false
for key, value := range data {
switch value := value.(type) {
case string:
if replaced, matched := pm.markString(value); matched {
found = true
data[key] = replaced
}
case []any:
if pm.markArray(value) {
found = true
}
case map[string]interface{}:
if pm.markMap(value) {
found = true
}
case int, float64, bool:
formatted := fmt.Sprintf("%v", value)
if pm.Regex.MatchString(formatted) {
data[key] = pm.Regex.ReplaceAllString(formatted, pm.MarkerStart+"$0"+pm.MarkerEnd)
found = true
}
default:
log.Debug().Type("type", value).Msg("unknown logEvent type inside markMap")
}
}
return found
}
func (pm *PatternMatcher) markMapString(orderedMap *orderedmap.OrderedMap[string, string]) bool {
found := false
for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() {
if replaced, matched := pm.markString(pair.Value); matched {
found = true
orderedMap.Set(pair.Key, replaced)
}
}
return found
}
func (pm *PatternMatcher) markArray(data []any) bool {
found := false
for i, value := range data {
switch value := value.(type) {
case string:
if replaced, matched := pm.markString(value); matched {
found = true
data[i] = replaced
}
case int, float64, bool:
formatted := fmt.Sprintf("%v", value)
if pm.Regex.MatchString(formatted) {
data[i] = pm.Regex.ReplaceAllString(formatted, pm.MarkerStart+"$0"+pm.MarkerEnd)
found = true
}
case []any:
if pm.markArray(value) {
found = true
}
case map[string]interface{}:
if pm.markMap(value) {
found = true
}
}
}
return found
}
func (pm *PatternMatcher) markString(value string) (string, bool) {
if pm.Regex.MatchString(value) {
replaced := pm.Regex.ReplaceAllString(value, pm.MarkerStart+"$0"+pm.MarkerEnd)
return replaced, true
}
return value, false
}