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  }