github.com/nikandfor/tlog@v0.21.5-0.20231108111739-3ef89426a96d/verbosity_filter.go (about) 1 package tlog 2 3 import ( 4 "path" 5 "regexp" 6 "strings" 7 "sync" 8 "sync/atomic" 9 "unsafe" 10 11 "github.com/nikandfor/loc" 12 ) 13 14 type ( 15 filter struct { 16 f string 17 18 mu sync.RWMutex `deep:"-"` 19 c map[fkey]bool `deep:"-"` 20 } 21 22 fkey struct { 23 pc loc.PC 24 topics string 25 } 26 ) 27 28 func SetVerbosity(vfilter string) { 29 DefaultLogger.SetVerbosity(vfilter) 30 } 31 32 func (l *Logger) SetVerbosity(vfilter string) { 33 var f *filter 34 35 if vfilter != "" { 36 f = &filter{ 37 f: vfilter, 38 c: make(map[fkey]bool), 39 } 40 } 41 42 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&l.filter)), unsafe.Pointer(f)) 43 } 44 45 func V(topics string) *Logger { 46 if DefaultLogger.ifv(0, topics) { 47 return DefaultLogger 48 } 49 50 return nil 51 } 52 53 func (l *Logger) V(topics string) *Logger { 54 if l.ifv(0, topics) { 55 return l 56 } 57 58 return nil 59 } 60 61 func If(topics string) bool { 62 return DefaultLogger.ifv(0, topics) 63 } 64 65 func (l *Logger) If(topics string) bool { 66 return l.ifv(0, topics) 67 } 68 69 func IfDepth(d int, topics string) bool { 70 return DefaultLogger.ifv(d, topics) 71 } 72 73 func (l *Logger) IfDepth(d int, topics string) bool { 74 return l.ifv(d, topics) 75 } 76 77 func (s Span) V(topics string) Span { 78 if s.Logger.ifv(0, topics) { 79 return s 80 } 81 82 return Span{} 83 } 84 85 func (s Span) If(topics string) bool { 86 return s.Logger.ifv(0, topics) 87 } 88 89 func (s Span) IfDepth(d int, topics string) bool { 90 return s.Logger.ifv(d, topics) 91 } 92 93 func (l *Logger) ifv(d int, topics string) bool { 94 if l == nil { 95 return false 96 } 97 98 f := l.getfilter() 99 if f == nil { 100 return false 101 } 102 103 if f.f == "*" { 104 return true 105 } 106 107 var pc loc.PC 108 caller1(2+d, &pc, 1, 1) 109 110 return f.match(pc, topics) 111 } 112 113 func (l *Logger) getfilter() *filter { 114 return (*filter)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&l.filter)))) 115 } 116 117 func (f *filter) match(pc loc.PC, topics string) (r bool) { 118 k := fkey{pc: pc, topics: topics} 119 120 f.mu.RLock() 121 r, ok := f.c[k] 122 f.mu.RUnlock() 123 124 if ok { 125 return r 126 } 127 128 defer f.mu.Unlock() 129 f.mu.Lock() 130 131 r, ok = f.c[k] 132 if ok { 133 return r 134 } 135 136 name, file, _ := pc.NameFileLine() 137 r = f.matchPattern(name, file, topics) 138 139 f.c[k] = r 140 141 return r 142 } 143 144 func (f *filter) matchPattern(name, file, topics string) (r bool) { 145 ts := strings.Split(topics, ",") 146 147 if f.f != "" && f.f[0] == '!' { 148 r = true 149 } 150 151 for _, ff := range strings.Split(f.f, ",") { 152 if ff == "" { 153 continue 154 } 155 156 set := ff[0] != '!' 157 ff = strings.TrimPrefix(ff, "!") 158 159 p := strings.IndexByte(ff, '=') 160 161 if p != -1 && ff[:p] != "" { 162 if !f.matchPath(ff[:p], file) && !f.matchType(ff[:p], name) { 163 continue 164 } 165 } 166 167 if !f.matchTopics(ff[p+1:], ts) { 168 continue 169 } 170 171 r = set 172 } 173 174 return r 175 } 176 177 func (f *filter) matchTopics(filt string, ts []string) bool { 178 for _, ff := range strings.Split(filt, "+") { 179 if ff == "" { 180 continue 181 } 182 if ff == "*" { 183 return true 184 } 185 186 for _, t := range ts { 187 if ff == t { 188 return true 189 } 190 } 191 } 192 193 return false 194 } 195 196 func (f *filter) matchPath(pt, file string) bool { 197 var b strings.Builder 198 for i, seg := range strings.Split(pt, "/") { 199 if seg == "" { 200 continue 201 } 202 203 if i != 0 { 204 b.WriteByte('/') 205 } 206 207 if seg == "*" { 208 b.WriteString(`.*`) 209 } else { 210 b.WriteString(regexp.QuoteMeta(seg)) 211 } 212 } 213 214 // Printf("file %v <- %v", b.String(), pattern) 215 216 re := regexp.MustCompile("(^|/)" + b.String() + "$") 217 218 return re.MatchString(file) || re.MatchString(path.Dir(file)) 219 } 220 221 func (f *filter) matchType(pt, name string) bool { 222 tp := path.Base(name) 223 224 var b strings.Builder 225 for i, n := range strings.Split(pt, ".") { 226 if i != 0 { 227 b.WriteByte('.') 228 } 229 230 switch { 231 case n == "*": 232 b.WriteString(`[\w\.]+`) 233 case regexp.MustCompile(`\(\*?\w+\)`).MatchString(n): 234 n = regexp.QuoteMeta(n) 235 b.WriteString(n) 236 case regexp.MustCompile(`[\w\*]+`).MatchString(n): 237 n = strings.ReplaceAll(n, "*", `.*`) 238 b.WriteString(n) 239 default: 240 return false 241 } 242 } 243 244 re := regexp.MustCompile(`(^|\.)` + b.String() + "\\b") 245 246 if re.MatchString(tp) { 247 return true 248 } 249 250 s := regexp.MustCompile(`(\w+)(\.\((\*?)(\w+)\))?\.((\w+)(\.\w+)*)`).FindStringSubmatch(tp) 251 s = s[1:] 252 253 if pt == s[0] { // pkg 254 return true 255 } 256 257 if s[1] == "" { // no (*Type) (It's function) 258 return false 259 } 260 261 if re.MatchString(s[0] + "." + s[2] + s[3]) { // pkg.*Type 262 return true 263 } 264 265 if re.MatchString(s[0] + "." + s[3]) { // pkg.*Type 266 return true 267 } 268 269 if re.MatchString(s[0] + "." + s[2] + s[3] + "." + s[4]) { // pkg.*Type.Func... 270 return true 271 } 272 273 if s[2] == "" { // * 274 return false 275 } 276 277 if re.MatchString(s[0] + "." + s[3] + "." + s[4]) { // Type 278 return true 279 } 280 281 if re.MatchString(s[0] + ".(" + s[3] + ")." + s[4]) { // (Type) 282 return true 283 } 284 285 // Printf("type %q <- %v %v", s, tp, pt) 286 287 return false 288 }