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  }