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