github.com/puellanivis/breton@v0.2.16/lib/glog/glog.go (about) 1 // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ (With no dependency injection, the code had to be modified to integrate with github.com/puellanivis/breton/lib/gnuflag) 2 // 3 // Copyright 2013 Google Inc. All Rights Reserved. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 // Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. 18 // It provides functions Info, Warning, Error, Fatal, plus formatting variants such as 19 // Infof. It also provides V-style logging controlled by the --verbosity and --vmodule=file=2 flags. 20 // 21 // Basic examples: 22 // 23 // glog.Info("Prepare to repel boarders") 24 // 25 // glog.Fatalf("Initialization failed: %s", err) 26 // 27 // See the documentation for the V function for an explanation of these examples: 28 // 29 // if glog.V(2) { 30 // log.Info("Starting transaction...") 31 // } 32 // 33 // glog.V(2).Infoln("Processed", nItems, "elements") 34 // 35 // Log output is buffered and written periodically using Flush. Programs 36 // should call Flush before exiting to guarantee all log output is written. 37 // 38 // By default, all log statements write to files in a temporary directory. 39 // This package provides several flags that modify this behavior. 40 // As a result, flag.Parse must be called before any logging is done. 41 // 42 // --logtostderr=false 43 // Logs are written to standard error instead of to files. 44 // --alsologtostderr=false 45 // Logs are written to standard error as well as to files. 46 // --stderrthreshold=ERROR 47 // Log events at or above this severity are logged to standard 48 // error as well as to files. 49 // --log_dir="" 50 // Log files will be written to this directory instead of the 51 // default temporary directory. 52 // 53 // Other flags provide aids to debugging. 54 // 55 // --log_backtrace_at="" 56 // When set to a file and line number holding a logging statement, 57 // such as 58 // --log_backtrace_at=gopherflakes.go:234 59 // a stack trace will be written to the Info log whenever execution 60 // hits that statement. (Unlike with --vmodule, the ".go" must be 61 // present.) 62 // --verbosity=0 63 // Enable V-leveled logging at the specified level. 64 // --vmodule="" 65 // The syntax of the argument is a comma-separated list of pattern=N, 66 // where pattern is a literal file name (minus the ".go" suffix) or 67 // "glob" pattern and N is a V level. For instance, 68 // --vmodule=gopher*=3 69 // sets the V level to 3 in all Go files whose names begin "gopher". 70 package glog 71 72 import ( 73 "bufio" 74 "bytes" 75 "fmt" 76 "io" 77 stdLog "log" 78 "os" 79 "runtime" 80 "strconv" 81 "strings" 82 "sync" 83 "sync/atomic" 84 "time" 85 86 flag "github.com/puellanivis/breton/lib/gnuflag" 87 ) 88 89 // flushSyncWriter is the interface satisfied by logging destinations. 90 type flushSyncWriter interface { 91 Flush() error 92 Sync() error 93 io.Writer 94 } 95 96 const flushInterval = 30 * time.Second 97 98 func init() { 99 go func() { 100 for range time.Tick(flushInterval) { 101 Flush() 102 } 103 }() 104 } 105 106 // loggingT collects all the global state of the logging setup. 107 type loggingT struct { 108 // This is used to synchronize logging. 109 sync.Mutex 110 111 flagT 112 113 // file holds writer for each of the log types. 114 file [numSeverity]flushSyncWriter 115 } 116 117 var logging loggingT 118 119 // Flush flushes all pending log I/O. 120 func Flush() { 121 logging.Lock() 122 defer logging.Unlock() 123 124 logging.flushAll() 125 } 126 127 // flushAll flushes all the logs and attempts to "sync" their data to disk. 128 // One is expected to be holding the loggingT.Mutex 129 func (l *loggingT) flushAll() { 130 // Flush from fatal down, in case there's trouble flushing. 131 for s := fatalLog; s >= infoLog; s-- { 132 if file := l.file[s]; file != nil { 133 _ = file.Flush() 134 _ = file.Sync() 135 } 136 } 137 } 138 139 var timeNow = time.Now // Stubbed out for testing. 140 141 /* 142 header formats a log header as defined by the C++ implementation. 143 It returns a buffer containing the formatted header and the user's file and line number. 144 The depth specifies how many stack frames above lives the source line to be identified in the log message. 145 146 Log lines have this form: 147 148 Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... 149 150 where the fields are defined as follows: 151 152 L A single character, representing the log level (eg 'I' for INFO) 153 mm The month (zero padded; ie May is '05') 154 dd The day (zero padded) 155 hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds 156 threadid The space-padded thread ID as returned by GetTID() 157 file The file name 158 line The line number 159 msg The user-supplied message 160 */ 161 func (l *loggingT) header(s severity, depth int) (*bytes.Buffer, string, int) { 162 _, file, line, ok := runtime.Caller(3 + depth) 163 if !ok { 164 file = "???" 165 line = 1 166 } 167 168 if slash := strings.LastIndexByte(file, '/'); slash >= 0 { 169 file = file[slash+1:] 170 } 171 172 return l.formatHeader(s, file, line), file, line 173 } 174 175 // formatHeader formats a log header using the provided file name and line number. 176 func (l *loggingT) formatHeader(s severity, file string, line int) *bytes.Buffer { 177 now := timeNow() 178 179 if line < 0 { 180 line = 0 // not a real line number, but acceptable to someDigits 181 } 182 183 if s > fatalLog { 184 s = infoLog // for safety. 185 } 186 buf := getBuffer() 187 188 // Thinks are simple enough, Fprintf uses a lot of reflection that is unnecessary, 189 // but bytes.Buffer already uses a small bootstrap array, 190 // and strconv.Itoa has some amazingly clever speed up to handle small values. 191 _, month, day := now.Date() 192 hour, minute, second := now.Clock() 193 ns := now.Nanosecond() / 1000 194 195 // Lmmdd hh:mm:ss.uuuuuu threadid file:line] 196 buf.WriteByte(severityChars[s]) 197 writeTwo(buf, int(month)) 198 writeTwo(buf, day) 199 buf.WriteByte(' ') 200 201 writeTwo(buf, hour) 202 buf.WriteByte(':') 203 writeTwo(buf, minute) 204 buf.WriteByte(':') 205 writeTwo(buf, second) 206 buf.WriteByte('.') 207 208 nano := strconv.Itoa(ns) 209 if len(nano) < 6 { 210 buf.WriteString("000000"[len(nano):]) 211 } 212 buf.WriteString(nano) 213 buf.WriteByte(' ') 214 215 buf.Write(tid) 216 buf.WriteByte(' ') 217 218 buf.WriteString(file) 219 buf.WriteByte(':') 220 221 buf.WriteString(strconv.Itoa(line)) 222 buf.WriteByte(']') 223 buf.WriteByte(' ') 224 225 return buf 226 } 227 228 func (l *loggingT) println(s severity, args ...interface{}) { 229 buf, file, line := l.header(s, 0) 230 fmt.Fprintln(buf, args...) 231 l.output(s, buf, file, line, false) 232 } 233 234 func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { 235 buf, file, line := l.header(s, depth) 236 237 fmt.Fprint(buf, args...) 238 ensureNL(buf) 239 240 l.output(s, buf, file, line, false) 241 } 242 243 func (l *loggingT) print(s severity, args ...interface{}) { 244 l.printDepth(s, 1, args...) 245 } 246 247 func (l *loggingT) printf(s severity, format string, args ...interface{}) { 248 buf, file, line := l.header(s, 0) 249 250 fmt.Fprintf(buf, format, args...) 251 ensureNL(buf) 252 253 l.output(s, buf, file, line, false) 254 } 255 256 // output writes the data to the log files and releases the buffer. 257 func (l *loggingT) output(s severity, buf *bytes.Buffer, file string, line int, alsoToStderr bool) { 258 if l.TraceLocation.isSet() { 259 if l.TraceLocation.match(file, line) { 260 buf.Write(stacks(false)) 261 } 262 } 263 data := buf.Bytes() 264 265 l.Lock() 266 267 if !flag.Parsed() { 268 os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) 269 os.Stderr.Write(data) 270 271 } else if l.ToStderr { 272 os.Stderr.Write(data) 273 274 } else { 275 if alsoToStderr || l.AlsoToStderr || s >= l.StderrThreshold.get() { 276 os.Stderr.Write(data) 277 } 278 279 if l.file[s] == nil { 280 if err := l.createFiles(s); err != nil { 281 os.Stderr.Write(data) // Make sure the message appears somewhere. 282 l.exit(err) 283 } 284 } 285 286 switch s { 287 case fatalLog: 288 _, _ = l.file[fatalLog].Write(data) 289 fallthrough 290 case errorLog: 291 _, _ = l.file[errorLog].Write(data) 292 fallthrough 293 case warningLog: 294 _, _ = l.file[warningLog].Write(data) 295 fallthrough 296 case infoLog: 297 _, _ = l.file[infoLog].Write(data) 298 } 299 } 300 301 if s == fatalLog { 302 // If we got here via Exit rather than Fatal, print no stacks. 303 if atomic.LoadUint32(&fatalNoStacks) > 0 { 304 l.Unlock() 305 306 timeoutFlush(10 * time.Second) 307 os.Exit(1) 308 } 309 310 // Dump all goroutine stacks before exiting. 311 // First, make sure we see the trace for the current goroutine on standard error. 312 // If --logtostderr has been specified, the loop below will do that anyway 313 // as the first stack in the full dump. 314 if !l.ToStderr { 315 os.Stderr.Write(stacks(false)) 316 } 317 318 // Write the stack trace for all goroutines to the files. 319 trace := stacks(true) 320 321 for log := fatalLog; log >= infoLog; log-- { 322 if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. 323 _, _ = f.Write(trace) 324 } 325 } 326 327 l.Unlock() 328 329 timeoutFlush(10 * time.Second) 330 os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. 331 } 332 333 l.Unlock() 334 335 putBuffer(buf) 336 if stats := severityStats[s]; stats != nil { 337 stats.add(int64(len(data))) 338 } 339 } 340 341 // timeoutFlush calls Flush and returns when it completes or after timeout 342 // elapses, whichever happens first. This is needed because the hooks invoked 343 // by Flush may deadlock when glog.Fatal is called from a hook that holds 344 // a lock. 345 func timeoutFlush(timeout time.Duration) { 346 done := make(chan struct{}) 347 go func() { 348 defer close(done) 349 350 Flush() // calls logging.lockAndFlushAll() 351 }() 352 353 select { 354 case <-done: 355 case <-time.After(timeout): 356 fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) 357 } 358 } 359 360 // stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. 361 func stacks(all bool) []byte { 362 // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. 363 n := 10000 364 if all { 365 n = 100000 366 } 367 var trace []byte 368 for i := 0; i < 5; i++ { 369 trace = make([]byte, n) 370 nbytes := runtime.Stack(trace, all) 371 if nbytes < len(trace) { 372 return trace[:nbytes] 373 } 374 n *= 2 375 } 376 return trace 377 } 378 379 // exit is called if there is trouble creating or writing log files. 380 // It flushes the logs and exits the program; there's no point in hanging around. 381 // logging Mutex is held. 382 func (l *loggingT) exit(err error) { 383 fmt.Fprintln(os.Stderr, "log: exiting because of error: ", err) 384 Flush() 385 os.Exit(2) 386 } 387 388 // syncBuffer joins a bufio.Writer to its underlying file, providing access to the 389 // file's Sync method and providing a wrapper for the Write method that provides log 390 // file rotation. There are conflicting methods, so the file cannot be embedded. 391 // logging Mutex is held for all its methods. 392 type syncBuffer struct { 393 logger *loggingT 394 sev severity 395 396 *bufio.Writer 397 file *os.File 398 nbytes uint64 // The number of bytes written to this file 399 } 400 401 func (sb *syncBuffer) Sync() error { 402 return sb.file.Sync() 403 } 404 405 func (sb *syncBuffer) Write(b []byte) (n int, err error) { 406 if sb.nbytes+uint64(len(b)) >= MaxSize { 407 if err := sb.rotateFile(time.Now()); err != nil { 408 sb.logger.exit(err) 409 } 410 } 411 412 n, err = sb.Writer.Write(b) 413 sb.nbytes += uint64(n) 414 if err != nil { 415 sb.logger.exit(err) 416 } 417 418 return n, err 419 } 420 421 // bufferSize sizes the buffer associated with each log file. It's large 422 // so that log records can accumulate without the logging thread blocking 423 // on disk I/O. The flushDaemon will block instead. 424 const bufferSize = 256 * 1024 425 426 // rotateFile closes the syncBuffer's file and starts a new one. 427 func (sb *syncBuffer) rotateFile(now time.Time) error { 428 if sb.file != nil { 429 if err := sb.Flush(); err != nil { 430 return err 431 } 432 433 if err := sb.file.Close(); err != nil { 434 return err 435 } 436 } 437 438 var err error 439 sb.file, _, err = create(severityNames[sb.sev], now) 440 sb.nbytes = 0 441 if err != nil { 442 return err 443 } 444 445 sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) 446 447 // Write header. 448 buf := new(bytes.Buffer) 449 450 fmt.Fprintf(buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) 451 fmt.Fprintf(buf, "Running on machine: %s\n", host) 452 fmt.Fprintf(buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) 453 fmt.Fprintf(buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") 454 455 n, err := buf.WriteTo(sb.file) 456 sb.nbytes += uint64(n) 457 return err 458 } 459 460 // createFiles creates all the log files for severity from sev down to infoLog. 461 // logging mutex is held. 462 func (l *loggingT) createFiles(sev severity) error { 463 now := time.Now() 464 465 // Files are created in decreasing severity order, so as soon as we find one 466 // has already been created, we can stop. 467 for s := sev; s >= infoLog && l.file[s] == nil; s-- { 468 sb := &syncBuffer{ 469 logger: l, 470 sev: s, 471 } 472 473 if err := sb.rotateFile(now); err != nil { 474 return err 475 } 476 477 l.file[s] = sb 478 } 479 480 return nil 481 } 482 483 // CopyStandardLogTo arranges for messages written to the Go "log" package's 484 // default logs to also appear in the Google logs for the named and lower 485 // severities. Subsequent changes to the standard log's default output location 486 // or format may break this behavior. 487 // 488 // Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not 489 // recognized, CopyStandardLogTo panics. 490 func CopyStandardLogTo(name string) { 491 sev, ok := severityByName[name] 492 if !ok { 493 panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) 494 } 495 496 // Set a log format that captures the user's file and line: 497 // d.go:23: message 498 stdLog.SetFlags(stdLog.Lshortfile) 499 stdLog.SetOutput(logBridge(sev)) 500 } 501 502 // logBridge provides the Write method that enables CopyStandardLogTo to connect 503 // Go's standard logs to the logs provided by this package. 504 type logBridge severity 505 506 // Write parses the standard logging line and passes its components to the 507 // logger for severity(lb). 508 func (lb logBridge) Write(b []byte) (n int, err error) { 509 file, line := "???", 1 510 var text string 511 512 // Split "d.go:23: message" into "d.go", "23", and "message". 513 if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { 514 text = fmt.Sprintf("bad log format: %s", b) 515 516 } else { 517 file = string(parts[0]) 518 text = string(parts[2][1:]) // skip leading space 519 520 line, err = strconv.Atoi(string(parts[1])) 521 if err != nil { 522 text = fmt.Sprintf("bad line number: %s", b) 523 line = 1 524 } 525 } 526 527 buf := logging.formatHeader(severity(lb), file, line) 528 529 buf.WriteString(text) 530 ensureNL(buf) 531 532 // l.output with alsoToStderr=true, so standard log messages 533 // always appear on standard error. 534 logging.output(severity(lb), buf, file, line, true) 535 536 return len(b), nil 537 }