github.com/puellanivis/breton@v0.2.16/lib/glog/flags.go (about) 1 package glog 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "path/filepath" 8 "runtime" 9 "strconv" 10 "strings" 11 "sync" 12 "sync/atomic" 13 14 flag "github.com/puellanivis/breton/lib/gnuflag" 15 ) 16 17 // severity identifies the sort of log: info, warning, etc. 18 // It also implements the flag.Value interface. 19 // The --stderrthreshold flag is of type severity and should be modified only through the flag.Value interface. 20 // The values match the corresponding constants in C++. 21 type severity int32 // sync/atomic int32 22 23 // These constants identify the log levels in order of increasing severity. 24 // A message written to a high-severity log file is also written to each 25 // lower-severity log file. 26 const ( 27 infoLog severity = iota 28 warningLog 29 errorLog 30 fatalLog 31 numSeverity = 4 32 ) 33 34 const severityChars = "IWEF" 35 36 var severityNames = []string{ 37 infoLog: "INFO", 38 warningLog: "WARNING", 39 errorLog: "ERROR", 40 fatalLog: "FATAL", 41 } 42 43 var severityByName = map[string]severity{ 44 "INFO": infoLog, 45 "WARNING": warningLog, 46 "ERROR": errorLog, 47 "FATAL": fatalLog, 48 } 49 50 // get returns the value of the severity. 51 func (s *severity) get() severity { 52 return severity(atomic.LoadInt32((*int32)(s))) 53 } 54 55 // set sets the value of the severity. 56 func (s *severity) set(val severity) { 57 atomic.StoreInt32((*int32)(s), int32(val)) 58 } 59 60 // String is part of the flag.Value interface. 61 func (s *severity) String() string { 62 return strconv.FormatInt(int64(s.get()), 10) 63 } 64 65 // Get is part of the flag.Value interface. 66 func (s *severity) Get() interface{} { 67 return s.get() 68 } 69 70 // Set is part of the flag.Value interface. 71 func (s *severity) Set(value string) error { 72 var threshold severity 73 74 key := strings.ToUpper(value) 75 threshold, ok := severityByName[key] 76 77 // If it is not a known name, then parse it as an int. 78 if !ok { 79 v, err := strconv.Atoi(value) 80 if err != nil { 81 return err 82 } 83 84 threshold = severity(v) 85 } 86 87 s.set(threshold) 88 return nil 89 } 90 91 // Level is exported because it appears in the arguments to V and is 92 // the type of the --verbosity flag, which can be set programmatically. 93 // It's a distinct type because we want to discriminate it from logType. 94 // Variables of type level are only changed under logging.mu. 95 // The --verbosity flag is read only with atomic ops, so the state of the logging 96 // module is consistent. 97 98 // Level is treated as a sync/atomic int32. 99 100 // Level specifies a level of verbosity for V logs. *Level implements 101 // flag.Value; the --verbosity flag is of type Level and should be modified 102 // only through the flag.Value interface. 103 type Level int32 // sync/atomic int32 104 105 // get returns the value of the Level. 106 func (l *Level) get() Level { 107 return Level(atomic.LoadInt32((*int32)(l))) 108 } 109 110 // set sets the value of the Level. 111 func (l *Level) set(val Level) { 112 atomic.StoreInt32((*int32)(l), int32(val)) 113 } 114 115 // String is part of the flag.Value interface. 116 func (l *Level) String() string { 117 return strconv.FormatInt(int64(l.get()), 10) 118 } 119 120 // Get is part of the flag.Value interface. 121 func (l *Level) Get() interface{} { 122 return l.get() 123 } 124 125 // Set is part of the flag.Value interface. 126 func (l *Level) Set(value string) error { 127 v, err := strconv.Atoi(value) 128 if err != nil { 129 return err 130 } 131 132 l.set(Level(v)) 133 return nil 134 } 135 136 // modulePat contains a filter for the --vmodule flag. 137 // It holds a verbosity level and a file pattern to match. 138 type modulePat struct { 139 pattern string 140 literal bool // The pattern is a literal string 141 level Level 142 } 143 144 // match reports whether the file matches the pattern. It uses a string 145 // comparison if the pattern contains no metacharacters. 146 func (m *modulePat) match(file string) bool { 147 if m.literal { 148 return file == m.pattern 149 } 150 151 match, _ := filepath.Match(m.pattern, file) 152 return match 153 } 154 155 // moduleSpec represents the setting of the --vmodule flag. 156 type moduleSpec struct { 157 sync.RWMutex 158 159 set int32 160 filters []modulePat 161 vmap map[uintptr]Level 162 } 163 164 func (m *moduleSpec) isSet() bool { 165 return atomic.LoadInt32(&m.set) > 0 166 } 167 168 func (m *moduleSpec) getV(pc uintptr) Level { 169 m.RLock() 170 v, ok := m.vmap[pc] 171 m.RUnlock() 172 173 if ok { 174 return v 175 } 176 177 return m.setV(pc) 178 } 179 180 // setV computes and memoizes the V level for a given PC when vmodule is enabled. 181 // File pattern matching takes the basename of the file, stripped of its .go suffix, 182 // and uses filepath.Match, which is a little more general than the *? matching in C++. 183 func (m *moduleSpec) setV(pc uintptr) Level { 184 frames := runtime.CallersFrames([]uintptr{pc}) 185 frame, _ := frames.Next() 186 187 // The file is something like /a/b/c/d.go. 188 // We just want the d. 189 file := strings.TrimSuffix(frame.File, ".go") 190 191 if slash := strings.LastIndexByte(file, '/'); slash >= 0 { 192 file = file[slash+1:] 193 } 194 195 m.Lock() 196 defer m.Unlock() 197 198 if v, ok := m.vmap[pc]; ok { 199 // someone did the work for us while we were waiting on the lock. 200 return v 201 } 202 203 for _, filter := range m.filters { 204 if filter.match(file) { 205 m.vmap[pc] = filter.level 206 return filter.level 207 } 208 } 209 210 m.vmap[pc] = 0 211 return 0 212 } 213 214 func (m *moduleSpec) String() string { 215 // Lock because the type is not atomic. TODO: clean this up. 216 m.RLock() 217 defer m.RUnlock() 218 219 b := new(bytes.Buffer) 220 for i, f := range m.filters { 221 if i > 0 { 222 b.WriteByte(',') 223 } 224 b.WriteString(f.pattern) 225 b.WriteByte('=') 226 b.WriteString(strconv.Itoa(int(f.level))) 227 } 228 229 return b.String() 230 } 231 232 // Get is part of the (Go 1.2) flag.Getter interface. 233 // It always returns nil for this flag type since the struct is not exported. 234 func (m *moduleSpec) Get() interface{} { 235 return nil 236 } 237 238 var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") 239 240 // Syntax: --vmodule=recordio=2,file=1,gfs*=3 241 func (m *moduleSpec) Set(value string) error { 242 var filters []modulePat 243 244 for _, pat := range strings.Split(value, ",") { 245 if len(pat) == 0 { 246 // Empty strings such as from a trailing comma can be ignored. 247 continue 248 } 249 250 patLev := strings.Split(pat, "=") 251 if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { 252 return errVmoduleSyntax 253 } 254 255 pattern := patLev[0] 256 257 v, err := strconv.Atoi(patLev[1]) 258 if err != nil { 259 return errors.New("syntax error: expect comma-separated list of filename=N") 260 } 261 262 if v < 0 { 263 return errors.New("negative value for vmodule level") 264 } 265 if v == 0 { 266 continue // Ignore. It's harmless but no point in paying the overhead. 267 } 268 269 literal, err := isLiteral(pattern) 270 if err != nil { 271 return err 272 } 273 274 // TODO: check syntax of filter? 275 filters = append(filters, modulePat{ 276 pattern: pattern, 277 literal: literal, 278 level: Level(v), 279 }) 280 } 281 282 m.filters = filters 283 m.vmap = make(map[uintptr]Level) 284 atomic.StoreInt32(&m.set, int32(len(filters))) 285 286 return nil 287 } 288 289 // isLiteral reports whether the pattern is a literal string, 290 // that is, has no metacharacters that require filepath.Match to be called to match the pattern. 291 // 292 // The error returned should be filepath.ErrBadPattern, 293 // but filepath should provide the check pattern for syntax, which it doesn’t. 294 func isLiteral(pattern string) (bool, error) { 295 return !strings.ContainsAny(pattern, `\*?[]`), nil 296 } 297 298 // traceLocation represents the setting of the --log_backtrace_at flag. 299 type traceLocation struct { 300 sync.RWMutex 301 302 file string 303 line int32 304 } 305 306 // isSet reports whether the trace location has been specified. 307 // logging.mu is held. 308 func (t *traceLocation) isSet() bool { 309 return atomic.LoadInt32(&t.line) > 0 310 } 311 312 // match reports whether the specified file and line matches the trace location. 313 // The argument file name is the full path, not the basename specified in the flag. 314 // logging.mu is held. 315 func (t *traceLocation) match(file string, line int) bool { 316 // Lock because the type is not atomic. TODO: clean this up. 317 t.RLock() 318 defer t.RUnlock() 319 320 if atomic.LoadInt32(&t.line) != int32(line) { 321 return false 322 } 323 324 if i := strings.LastIndex(file, "/"); i >= 0 { 325 file = file[i+1:] 326 } 327 328 return t.file == file 329 } 330 331 func (t *traceLocation) String() string { 332 // Lock because the type is not atomic. TODO: clean this up. 333 t.RLock() 334 defer t.RUnlock() 335 336 return fmt.Sprintf("%s:%d", t.file, atomic.LoadInt32(&t.line)) 337 } 338 339 // Get is part of the (Go 1.2) flag.Getter interface. 340 // It always returns nil for this flag type since the struct is not exported. 341 func (t *traceLocation) Get() interface{} { 342 return nil 343 } 344 345 var errTraceSyntax = errors.New("syntax error: expect file.go:234") 346 347 // Syntax: --log_backtrace_at=gopherflakes.go:234 348 // Note that unlike vmodule the file extension is included here. 349 func (t *traceLocation) Set(value string) error { 350 t.Lock() 351 defer t.Unlock() 352 353 if value == "" { 354 // Unset. 355 t.line = 0 356 t.file = "" 357 358 return nil 359 } 360 361 fields := strings.Split(value, ":") 362 if len(fields) != 2 { 363 return errTraceSyntax 364 } 365 366 file, line := fields[0], fields[1] 367 if !strings.Contains(file, ".") { 368 return errTraceSyntax 369 } 370 371 v, err := strconv.Atoi(line) 372 if err != nil { 373 return errTraceSyntax 374 } 375 376 if v <= 0 { 377 return errors.New("negative or zero value for level") 378 } 379 380 t.file = file 381 atomic.StoreInt32(&t.line, int32(v)) 382 383 return nil 384 } 385 386 var _ = flag.Struct("", &logging.flagT) 387 388 // flagT collects all the flags of the logging setup. 389 type flagT struct { 390 // Boolean flags. NOT ATOMIC or thread-safe. 391 ToStderr bool `flag:"logtostderr" desc:"log to standard error instead of files"` 392 AlsoToStderr bool `flag:"alsologtostderr" desc:"log to standard error as well as files"` 393 394 // Level flag. Handled atomically. 395 StderrThreshold severity `flag:"stderrthreshold,def=ERROR" desc:"logs at or above this ·threshold· go to stderr"` 396 397 // traceLocation is the state of the --log_backtrace_at flag. 398 TraceLocation traceLocation `flag:"log_backtrace_at" desc:"when logging hits line ·file:N·, emit a stack trace"` 399 // These flags are modified only under lock, 400 // although verbosity may be fetched safely using atomic.LoadInt32. 401 Vmodule moduleSpec `flag:"vmodules" desc:"comma-separated list of ·pattern=N· settings for file-filtered logging"` 402 Verbosity Level `flag:"verbosity" desc:"log ·level· for V logs"` 403 }