package mlogger import ( "fmt" "strings" "go.uber.org/zap" ) type Logger = *zap.Logger // Named appends logger name components while avoiding adjacent or already-present // name stutter such as "notification.notification" or "vault.vault.kv". func Named(logger Logger, names ...any) Logger { if logger == nil { logger = zap.NewNop() } current := splitName(logger.Name()) for _, name := range names { if name == nil { continue } parts := splitName(fmt.Sprint(name)) if len(parts) == 0 { continue } if containsParts(current, parts) { continue } parts = trimOverlappingPrefix(current, parts) parts = trimStutteringPrefix(current, parts) if len(parts) == 0 { continue } child := strings.Join(parts, ".") logger = logger.Named(child) current = append(current, parts...) } return logger } func splitName(name string) []string { raw := strings.Split(name, ".") parts := make([]string, 0, len(raw)) for _, part := range raw { part = strings.TrimSpace(part) if part == "" { continue } parts = append(parts, part) } return parts } func containsParts(parent, child []string) bool { if len(parent) == 0 || len(child) == 0 || len(child) > len(parent) { return false } for i := 0; i <= len(parent)-len(child); i++ { matches := true for j := range child { if parent[i+j] != child[j] { matches = false break } } if matches { return true } } return false } func trimOverlappingPrefix(parent, child []string) []string { max := len(parent) if len(child) < max { max = len(child) } for n := max; n > 0; n-- { matches := true for i := 0; i < n; i++ { if parent[len(parent)-n+i] != child[i] { matches = false break } } if matches { return child[n:] } } return child } func trimStutteringPrefix(parent, child []string) []string { if len(parent) == 0 || len(child) == 0 { return child } last := parent[len(parent)-1] first := child[0] for _, separator := range []string{"_", "-"} { prefix := last + separator if !strings.HasPrefix(first, prefix) { continue } trimmed := strings.TrimSpace(strings.TrimPrefix(first, prefix)) if trimmed == "" { return child[1:] } out := append([]string{trimmed}, child[1:]...) return out } return child }