github.com/evdatsion/aphelion-dpos-bft@v0.32.1/libs/log/filter.go (about) 1 package log 2 3 import "fmt" 4 5 type level byte 6 7 const ( 8 levelDebug level = 1 << iota 9 levelInfo 10 levelError 11 ) 12 13 type filter struct { 14 next Logger 15 allowed level // XOR'd levels for default case 16 initiallyAllowed level // XOR'd levels for initial case 17 allowedKeyvals map[keyval]level // When key-value match, use this level 18 } 19 20 type keyval struct { 21 key interface{} 22 value interface{} 23 } 24 25 // NewFilter wraps next and implements filtering. See the commentary on the 26 // Option functions for a detailed description of how to configure levels. If 27 // no options are provided, all leveled log events created with Debug, Info or 28 // Error helper methods are squelched. 29 func NewFilter(next Logger, options ...Option) Logger { 30 l := &filter{ 31 next: next, 32 allowedKeyvals: make(map[keyval]level), 33 } 34 for _, option := range options { 35 option(l) 36 } 37 l.initiallyAllowed = l.allowed 38 return l 39 } 40 41 func (l *filter) Info(msg string, keyvals ...interface{}) { 42 levelAllowed := l.allowed&levelInfo != 0 43 if !levelAllowed { 44 return 45 } 46 l.next.Info(msg, keyvals...) 47 } 48 49 func (l *filter) Debug(msg string, keyvals ...interface{}) { 50 levelAllowed := l.allowed&levelDebug != 0 51 if !levelAllowed { 52 return 53 } 54 l.next.Debug(msg, keyvals...) 55 } 56 57 func (l *filter) Error(msg string, keyvals ...interface{}) { 58 levelAllowed := l.allowed&levelError != 0 59 if !levelAllowed { 60 return 61 } 62 l.next.Error(msg, keyvals...) 63 } 64 65 // With implements Logger by constructing a new filter with a keyvals appended 66 // to the logger. 67 // 68 // If custom level was set for a keyval pair using one of the 69 // Allow*With methods, it is used as the logger's level. 70 // 71 // Examples: 72 // logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto")) 73 // logger.With("module", "crypto").Info("Hello") # produces "I... Hello module=crypto" 74 // 75 // logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto"), log.AllowNoneWith("user", "Sam")) 76 // logger.With("module", "crypto", "user", "Sam").Info("Hello") # returns nil 77 // 78 // logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto"), log.AllowNoneWith("user", "Sam")) 79 // logger.With("user", "Sam").With("module", "crypto").Info("Hello") # produces "I... Hello module=crypto user=Sam" 80 func (l *filter) With(keyvals ...interface{}) Logger { 81 keyInAllowedKeyvals := false 82 83 for i := len(keyvals) - 2; i >= 0; i -= 2 { 84 for kv, allowed := range l.allowedKeyvals { 85 if keyvals[i] == kv.key { 86 keyInAllowedKeyvals = true 87 // Example: 88 // logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto")) 89 // logger.With("module", "crypto") 90 if keyvals[i+1] == kv.value { 91 return &filter{ 92 next: l.next.With(keyvals...), 93 allowed: allowed, // set the desired level 94 allowedKeyvals: l.allowedKeyvals, 95 initiallyAllowed: l.initiallyAllowed, 96 } 97 } 98 } 99 } 100 } 101 102 // Example: 103 // logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto")) 104 // logger.With("module", "main") 105 if keyInAllowedKeyvals { 106 return &filter{ 107 next: l.next.With(keyvals...), 108 allowed: l.initiallyAllowed, // return back to initially allowed 109 allowedKeyvals: l.allowedKeyvals, 110 initiallyAllowed: l.initiallyAllowed, 111 } 112 } 113 114 return &filter{ 115 next: l.next.With(keyvals...), 116 allowed: l.allowed, // simply continue with the current level 117 allowedKeyvals: l.allowedKeyvals, 118 initiallyAllowed: l.initiallyAllowed, 119 } 120 } 121 122 //-------------------------------------------------------------------------------- 123 124 // Option sets a parameter for the filter. 125 type Option func(*filter) 126 127 // AllowLevel returns an option for the given level or error if no option exist 128 // for such level. 129 func AllowLevel(lvl string) (Option, error) { 130 switch lvl { 131 case "debug": 132 return AllowDebug(), nil 133 case "info": 134 return AllowInfo(), nil 135 case "error": 136 return AllowError(), nil 137 case "none": 138 return AllowNone(), nil 139 default: 140 return nil, fmt.Errorf("Expected either \"info\", \"debug\", \"error\" or \"none\" level, given %s", lvl) 141 } 142 } 143 144 // AllowAll is an alias for AllowDebug. 145 func AllowAll() Option { 146 return AllowDebug() 147 } 148 149 // AllowDebug allows error, info and debug level log events to pass. 150 func AllowDebug() Option { 151 return allowed(levelError | levelInfo | levelDebug) 152 } 153 154 // AllowInfo allows error and info level log events to pass. 155 func AllowInfo() Option { 156 return allowed(levelError | levelInfo) 157 } 158 159 // AllowError allows only error level log events to pass. 160 func AllowError() Option { 161 return allowed(levelError) 162 } 163 164 // AllowNone allows no leveled log events to pass. 165 func AllowNone() Option { 166 return allowed(0) 167 } 168 169 func allowed(allowed level) Option { 170 return func(l *filter) { l.allowed = allowed } 171 } 172 173 // AllowDebugWith allows error, info and debug level log events to pass for a specific key value pair. 174 func AllowDebugWith(key interface{}, value interface{}) Option { 175 return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = levelError | levelInfo | levelDebug } 176 } 177 178 // AllowInfoWith allows error and info level log events to pass for a specific key value pair. 179 func AllowInfoWith(key interface{}, value interface{}) Option { 180 return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = levelError | levelInfo } 181 } 182 183 // AllowErrorWith allows only error level log events to pass for a specific key value pair. 184 func AllowErrorWith(key interface{}, value interface{}) Option { 185 return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = levelError } 186 } 187 188 // AllowNoneWith allows no leveled log events to pass for a specific key value pair. 189 func AllowNoneWith(key interface{}, value interface{}) Option { 190 return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = 0 } 191 }