// rotateFile closes the syncBuffer's file and starts a new one. func (sb *syncBuffer) rotateFile(now time.Time) error { if sb.file != nil { if err := sb.Flush(); err != nil { return err } if err := sb.file.Close(); err != nil { return err } } var err error sb.file, _, err = create(sb.sev, now) sb.nbytes = 0 if err != nil { return err } sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) f, l, _ := caller.Lookup(1) for _, msg := range []string{ fmt.Sprintf("[config] file created at: %s\n", now.Format("2006/01/02 15:04:05")), fmt.Sprintf("[config] running on machine: %s\n", host), fmt.Sprintf("[config] binary: %s\n", build.GetInfo().Short()), fmt.Sprintf("[config] arguments: %s\n", os.Args), fmt.Sprintf("line format: [IWEF]yymmdd hh:mm:ss.uuuuuu goid file:line msg\n"), } { buf := formatLogEntry(Entry{ Severity: sb.sev, Time: now.UnixNano(), Goroutine: goid.Get(), File: f, Line: int64(l), Message: msg, }, nil, nil) var n int n, err = sb.file.Write(buf.Bytes()) sb.nbytes += uint64(n) if err != nil { return err } logging.putBuffer(buf) } return nil }
// outputLogEntry marshals a log entry proto into bytes, and writes // the data to the log files. If a trace location is set, stack traces // are added to the entry before marshaling. func (l *loggingT) outputLogEntry(s Severity, file string, line int, msg string) { // TODO(tschottdorf): this is a pretty horrible critical section. l.mu.Lock() // Set additional details in log entry. now := time.Now() entry := Entry{ Severity: s, Time: now.UnixNano(), Goroutine: goid.Get(), File: file, Line: int64(line), Message: msg, } // On fatal log, set all stacks. var stacks []byte if s == Severity_FATAL { switch traceback { case tracebackSingle: stacks = getStacks(false) case tracebackAll: stacks = getStacks(true) } logExitFunc = func(error) {} // If we get a write error, we'll still exit. } else if l.traceLocation.isSet() { if l.traceLocation.match(file, line) { stacks = getStacks(false) } } if l.toStderr || !logDir.isSet() { l.outputToStderr(entry, stacks) } else { if s >= l.stderrThreshold.get() { l.outputToStderr(entry, stacks) } if l.file[s] == nil { if err := l.createFiles(s); err != nil { // Make sure the message appears somewhere. l.outputToStderr(entry, stacks) l.mu.Unlock() l.exit(err) return } } buf := l.processForFile(entry, stacks) data := buf.Bytes() switch s { case Severity_FATAL: if _, err := l.file[Severity_FATAL].Write(data); err != nil { panic(err) } fallthrough case Severity_ERROR: if _, err := l.file[Severity_ERROR].Write(data); err != nil { panic(err) } fallthrough case Severity_WARNING: if _, err := l.file[Severity_WARNING].Write(data); err != nil { panic(err) } fallthrough case Severity_INFO: if _, err := l.file[Severity_INFO].Write(data); err != nil { panic(err) } } l.putBuffer(buf) if stats := severityStats[s]; stats != nil { atomic.AddInt64(&stats.lines, 1) atomic.AddInt64(&stats.bytes, int64(len(data))) } } exitFunc := l.exitFunc l.mu.Unlock() // Flush and exit on fatal logging. if s == Severity_FATAL { // If we got here via Exit rather than Fatal, print no stacks. timeoutFlush(10 * time.Second) if atomic.LoadUint32(&fatalNoStacks) > 0 { exitFunc(1) } else { exitFunc(255) // C++ uses -1, which is silly because it's anded with 255 anyway. } } }