github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/log/handler_glog.go (about) 1 package log 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "runtime" 8 "strconv" 9 "strings" 10 "sync" 11 "sync/atomic" 12 ) 13 14 var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N") 15 16 var errTraceSyntax = errors.New("expect file.go:234") 17 18 type GlogHandler struct { 19 origin Handler 20 21 level uint32 22 override uint32 23 backtrace uint32 24 25 patterns []pattern 26 siteCache map[uintptr]Lvl 27 location string 28 lock sync.RWMutex 29 } 30 31 func NewGlogHandler(h Handler) *GlogHandler { 32 return &GlogHandler{ 33 origin: h, 34 } 35 } 36 37 func (h *GlogHandler) SetHandler(nh Handler) { 38 h.origin = nh 39 } 40 41 type pattern struct { 42 pattern *regexp.Regexp 43 level Lvl 44 } 45 46 func (h *GlogHandler) Verbosity(level Lvl) { 47 atomic.StoreUint32(&h.level, uint32(level)) 48 } 49 50 func (h *GlogHandler) Vmodule(ruleset string) error { 51 var filter []pattern 52 for _, rule := range strings.Split(ruleset, ",") { 53 54 if len(rule) == 0 { 55 continue 56 } 57 58 parts := strings.Split(rule, "=") 59 if len(parts) != 2 { 60 return errVmoduleSyntax 61 } 62 parts[0] = strings.TrimSpace(parts[0]) 63 parts[1] = strings.TrimSpace(parts[1]) 64 if len(parts[0]) == 0 || len(parts[1]) == 0 { 65 return errVmoduleSyntax 66 } 67 68 level, err := strconv.Atoi(parts[1]) 69 if err != nil { 70 return errVmoduleSyntax 71 } 72 if level <= 0 { 73 continue 74 } 75 76 matcher := ".*" 77 for _, comp := range strings.Split(parts[0], "/") { 78 if comp == "*" { 79 matcher += "(/.*)?" 80 } else if comp != "" { 81 matcher += "/" + regexp.QuoteMeta(comp) 82 } 83 } 84 if !strings.HasSuffix(parts[0], ".go") { 85 matcher += "/[^/]+\\.go" 86 } 87 matcher = matcher + "$" 88 89 re, _ := regexp.Compile(matcher) 90 filter = append(filter, pattern{re, Lvl(level)}) 91 } 92 93 h.lock.Lock() 94 defer h.lock.Unlock() 95 96 h.patterns = filter 97 h.siteCache = make(map[uintptr]Lvl) 98 atomic.StoreUint32(&h.override, uint32(len(filter))) 99 100 return nil 101 } 102 103 func (h *GlogHandler) BacktraceAt(location string) error { 104 105 parts := strings.Split(location, ":") 106 if len(parts) != 2 { 107 return errTraceSyntax 108 } 109 parts[0] = strings.TrimSpace(parts[0]) 110 parts[1] = strings.TrimSpace(parts[1]) 111 if len(parts[0]) == 0 || len(parts[1]) == 0 { 112 return errTraceSyntax 113 } 114 115 if !strings.HasSuffix(parts[0], ".go") { 116 return errTraceSyntax 117 } 118 if _, err := strconv.Atoi(parts[1]); err != nil { 119 return errTraceSyntax 120 } 121 122 h.lock.Lock() 123 defer h.lock.Unlock() 124 125 h.location = location 126 atomic.StoreUint32(&h.backtrace, uint32(len(location))) 127 128 return nil 129 } 130 131 func (h *GlogHandler) Log(r *Record) error { 132 133 if atomic.LoadUint32(&h.backtrace) > 0 { 134 135 h.lock.RLock() 136 match := h.location == r.Call.String() 137 h.lock.RUnlock() 138 139 if match { 140 141 r.Lvl = LvlInfo 142 143 buf := make([]byte, 1024*1024) 144 buf = buf[:runtime.Stack(buf, true)] 145 r.Msg += "\n\n" + string(buf) 146 } 147 } 148 149 if atomic.LoadUint32(&h.level) >= uint32(r.Lvl) { 150 return h.origin.Log(r) 151 } 152 153 if atomic.LoadUint32(&h.override) == 0 { 154 return nil 155 } 156 157 h.lock.RLock() 158 lvl, ok := h.siteCache[r.Call.PC()] 159 h.lock.RUnlock() 160 161 if !ok { 162 h.lock.Lock() 163 for _, rule := range h.patterns { 164 if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) { 165 h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true 166 break 167 } 168 } 169 170 if !ok { 171 h.siteCache[r.Call.PC()] = 0 172 } 173 h.lock.Unlock() 174 } 175 if lvl >= r.Lvl { 176 return h.origin.Log(r) 177 } 178 return nil 179 }