|
@@ -14,32 +14,32 @@
|
|
// See the License for the specific language governing permissions and
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// limitations under the License.
|
|
|
|
|
|
-// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
|
|
|
|
|
|
+// Package klog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
|
|
// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
|
|
// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
|
|
// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
|
|
// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
|
|
//
|
|
//
|
|
// Basic examples:
|
|
// Basic examples:
|
|
//
|
|
//
|
|
-// glog.Info("Prepare to repel boarders")
|
|
|
|
|
|
+// klog.Info("Prepare to repel boarders")
|
|
//
|
|
//
|
|
-// glog.Fatalf("Initialization failed: %s", err)
|
|
|
|
|
|
+// klog.Fatalf("Initialization failed: %s", err)
|
|
//
|
|
//
|
|
// See the documentation for the V function for an explanation of these examples:
|
|
// See the documentation for the V function for an explanation of these examples:
|
|
//
|
|
//
|
|
-// if glog.V(2) {
|
|
|
|
-// glog.Info("Starting transaction...")
|
|
|
|
|
|
+// if klog.V(2) {
|
|
|
|
+// klog.Info("Starting transaction...")
|
|
// }
|
|
// }
|
|
//
|
|
//
|
|
-// glog.V(2).Infoln("Processed", nItems, "elements")
|
|
|
|
|
|
+// klog.V(2).Infoln("Processed", nItems, "elements")
|
|
//
|
|
//
|
|
// Log output is buffered and written periodically using Flush. Programs
|
|
// Log output is buffered and written periodically using Flush. Programs
|
|
// should call Flush before exiting to guarantee all log output is written.
|
|
// should call Flush before exiting to guarantee all log output is written.
|
|
//
|
|
//
|
|
-// By default, all log statements write to files in a temporary directory.
|
|
|
|
|
|
+// By default, all log statements write to standard error.
|
|
// This package provides several flags that modify this behavior.
|
|
// This package provides several flags that modify this behavior.
|
|
// As a result, flag.Parse must be called before any logging is done.
|
|
// As a result, flag.Parse must be called before any logging is done.
|
|
//
|
|
//
|
|
-// -logtostderr=false
|
|
|
|
|
|
+// -logtostderr=true
|
|
// Logs are written to standard error instead of to files.
|
|
// Logs are written to standard error instead of to files.
|
|
// -alsologtostderr=false
|
|
// -alsologtostderr=false
|
|
// Logs are written to standard error as well as to files.
|
|
// Logs are written to standard error as well as to files.
|
|
@@ -68,7 +68,7 @@
|
|
// -vmodule=gopher*=3
|
|
// -vmodule=gopher*=3
|
|
// sets the V level to 3 in all Go files whose names begin "gopher".
|
|
// sets the V level to 3 in all Go files whose names begin "gopher".
|
|
//
|
|
//
|
|
-package glog
|
|
|
|
|
|
+package klog
|
|
|
|
|
|
import (
|
|
import (
|
|
"bufio"
|
|
"bufio"
|
|
@@ -78,6 +78,7 @@ import (
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
stdLog "log"
|
|
stdLog "log"
|
|
|
|
+ "math"
|
|
"os"
|
|
"os"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
"runtime"
|
|
"runtime"
|
|
@@ -141,7 +142,7 @@ func (s *severity) Set(value string) error {
|
|
if v, ok := severityByName(value); ok {
|
|
if v, ok := severityByName(value); ok {
|
|
threshold = v
|
|
threshold = v
|
|
} else {
|
|
} else {
|
|
- v, err := strconv.Atoi(value)
|
|
|
|
|
|
+ v, err := strconv.ParseInt(value, 10, 32)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
@@ -225,7 +226,7 @@ func (l *Level) Get() interface{} {
|
|
|
|
|
|
// Set is part of the flag.Value interface.
|
|
// Set is part of the flag.Value interface.
|
|
func (l *Level) Set(value string) error {
|
|
func (l *Level) Set(value string) error {
|
|
- v, err := strconv.Atoi(value)
|
|
|
|
|
|
+ v, err := strconv.ParseInt(value, 10, 32)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
@@ -293,7 +294,7 @@ func (m *moduleSpec) Set(value string) error {
|
|
return errVmoduleSyntax
|
|
return errVmoduleSyntax
|
|
}
|
|
}
|
|
pattern := patLev[0]
|
|
pattern := patLev[0]
|
|
- v, err := strconv.Atoi(patLev[1])
|
|
|
|
|
|
+ v, err := strconv.ParseInt(patLev[1], 10, 32)
|
|
if err != nil {
|
|
if err != nil {
|
|
return errors.New("syntax error: expect comma-separated list of filename=N")
|
|
return errors.New("syntax error: expect comma-separated list of filename=N")
|
|
}
|
|
}
|
|
@@ -395,21 +396,43 @@ type flushSyncWriter interface {
|
|
io.Writer
|
|
io.Writer
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// init sets up the defaults and runs flushDaemon.
|
|
func init() {
|
|
func init() {
|
|
- flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files")
|
|
|
|
- flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
|
|
|
|
- flag.Var(&logging.verbosity, "v", "log level for V logs")
|
|
|
|
- flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
|
|
|
|
- flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
|
|
|
|
- flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
|
|
|
|
-
|
|
|
|
- // Default stderrThreshold is ERROR.
|
|
|
|
- logging.stderrThreshold = errorLog
|
|
|
|
-
|
|
|
|
|
|
+ logging.stderrThreshold = errorLog // Default stderrThreshold is ERROR.
|
|
logging.setVState(0, nil, false)
|
|
logging.setVState(0, nil, false)
|
|
|
|
+ logging.logDir = ""
|
|
|
|
+ logging.logFile = ""
|
|
|
|
+ logging.logFileMaxSizeMB = 1800
|
|
|
|
+ logging.toStderr = true
|
|
|
|
+ logging.alsoToStderr = false
|
|
|
|
+ logging.skipHeaders = false
|
|
|
|
+ logging.addDirHeader = false
|
|
|
|
+ logging.skipLogHeaders = false
|
|
go logging.flushDaemon()
|
|
go logging.flushDaemon()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// InitFlags is for explicitly initializing the flags.
|
|
|
|
+func InitFlags(flagset *flag.FlagSet) {
|
|
|
|
+ if flagset == nil {
|
|
|
|
+ flagset = flag.CommandLine
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ flagset.StringVar(&logging.logDir, "log_dir", logging.logDir, "If non-empty, write log files in this directory")
|
|
|
|
+ flagset.StringVar(&logging.logFile, "log_file", logging.logFile, "If non-empty, use this log file")
|
|
|
|
+ flagset.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", logging.logFileMaxSizeMB,
|
|
|
|
+ "Defines the maximum size a log file can grow to. Unit is megabytes. "+
|
|
|
|
+ "If the value is 0, the maximum file size is unlimited.")
|
|
|
|
+ flagset.BoolVar(&logging.toStderr, "logtostderr", logging.toStderr, "log to standard error instead of files")
|
|
|
|
+ flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", logging.alsoToStderr, "log to standard error as well as files")
|
|
|
|
+ flagset.Var(&logging.verbosity, "v", "number for the log level verbosity")
|
|
|
|
+ flagset.BoolVar(&logging.skipHeaders, "add_dir_header", logging.addDirHeader, "If true, adds the file directory to the header")
|
|
|
|
+ flagset.BoolVar(&logging.skipHeaders, "skip_headers", logging.skipHeaders, "If true, avoid header prefixes in the log messages")
|
|
|
|
+ flagset.BoolVar(&logging.skipLogHeaders, "skip_log_headers", logging.skipLogHeaders, "If true, avoid headers when opening log files")
|
|
|
|
+ flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
|
|
|
|
+ flagset.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
|
|
|
|
+ flagset.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
|
|
|
|
+}
|
|
|
|
+
|
|
// Flush flushes all pending log I/O.
|
|
// Flush flushes all pending log I/O.
|
|
func Flush() {
|
|
func Flush() {
|
|
logging.lockAndFlushAll()
|
|
logging.lockAndFlushAll()
|
|
@@ -453,6 +476,27 @@ type loggingT struct {
|
|
// safely using atomic.LoadInt32.
|
|
// safely using atomic.LoadInt32.
|
|
vmodule moduleSpec // The state of the -vmodule flag.
|
|
vmodule moduleSpec // The state of the -vmodule flag.
|
|
verbosity Level // V logging level, the value of the -v flag/
|
|
verbosity Level // V logging level, the value of the -v flag/
|
|
|
|
+
|
|
|
|
+ // If non-empty, overrides the choice of directory in which to write logs.
|
|
|
|
+ // See createLogDirs for the full list of possible destinations.
|
|
|
|
+ logDir string
|
|
|
|
+
|
|
|
|
+ // If non-empty, specifies the path of the file to write logs. mutually exclusive
|
|
|
|
+ // with the log-dir option.
|
|
|
|
+ logFile string
|
|
|
|
+
|
|
|
|
+ // When logFile is specified, this limiter makes sure the logFile won't exceeds a certain size. When exceeds, the
|
|
|
|
+ // logFile will be cleaned up. If this value is 0, no size limitation will be applied to logFile.
|
|
|
|
+ logFileMaxSizeMB uint64
|
|
|
|
+
|
|
|
|
+ // If true, do not add the prefix headers, useful when used with SetOutput
|
|
|
|
+ skipHeaders bool
|
|
|
|
+
|
|
|
|
+ // If true, do not add the headers to log files
|
|
|
|
+ skipLogHeaders bool
|
|
|
|
+
|
|
|
|
+ // If true, add the file directory to the header
|
|
|
|
+ addDirHeader bool
|
|
}
|
|
}
|
|
|
|
|
|
// buffer holds a byte Buffer for reuse. The zero value is ready for use.
|
|
// buffer holds a byte Buffer for reuse. The zero value is ready for use.
|
|
@@ -538,9 +582,14 @@ func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
|
|
file = "???"
|
|
file = "???"
|
|
line = 1
|
|
line = 1
|
|
} else {
|
|
} else {
|
|
- slash := strings.LastIndex(file, "/")
|
|
|
|
- if slash >= 0 {
|
|
|
|
- file = file[slash+1:]
|
|
|
|
|
|
+ if slash := strings.LastIndex(file, "/"); slash >= 0 {
|
|
|
|
+ path := file
|
|
|
|
+ file = path[slash+1:]
|
|
|
|
+ if l.addDirHeader {
|
|
|
|
+ if dirsep := strings.LastIndex(path[:slash], "/"); dirsep >= 0 {
|
|
|
|
+ file = path[dirsep+1:]
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return l.formatHeader(s, file, line), file, line
|
|
return l.formatHeader(s, file, line), file, line
|
|
@@ -556,6 +605,9 @@ func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
|
|
s = infoLog // for safety.
|
|
s = infoLog // for safety.
|
|
}
|
|
}
|
|
buf := l.getBuffer()
|
|
buf := l.getBuffer()
|
|
|
|
+ if l.skipHeaders {
|
|
|
|
+ return buf
|
|
|
|
+ }
|
|
|
|
|
|
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
|
|
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
|
|
// It's worth about 3X. Fprintf is hard.
|
|
// It's worth about 3X. Fprintf is hard.
|
|
@@ -667,6 +719,49 @@ func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToSt
|
|
l.output(s, buf, file, line, alsoToStderr)
|
|
l.output(s, buf, file, line, alsoToStderr)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// redirectBuffer is used to set an alternate destination for the logs
|
|
|
|
+type redirectBuffer struct {
|
|
|
|
+ w io.Writer
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (rb *redirectBuffer) Sync() error {
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (rb *redirectBuffer) Flush() error {
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) {
|
|
|
|
+ return rb.w.Write(bytes)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// SetOutput sets the output destination for all severities
|
|
|
|
+func SetOutput(w io.Writer) {
|
|
|
|
+ logging.mu.Lock()
|
|
|
|
+ defer logging.mu.Unlock()
|
|
|
|
+ for s := fatalLog; s >= infoLog; s-- {
|
|
|
|
+ rb := &redirectBuffer{
|
|
|
|
+ w: w,
|
|
|
|
+ }
|
|
|
|
+ logging.file[s] = rb
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// SetOutputBySeverity sets the output destination for specific severity
|
|
|
|
+func SetOutputBySeverity(name string, w io.Writer) {
|
|
|
|
+ logging.mu.Lock()
|
|
|
|
+ defer logging.mu.Unlock()
|
|
|
|
+ sev, ok := severityByName(name)
|
|
|
|
+ if !ok {
|
|
|
|
+ panic(fmt.Sprintf("SetOutputBySeverity(%q): unrecognized severity name", name))
|
|
|
|
+ }
|
|
|
|
+ rb := &redirectBuffer{
|
|
|
|
+ w: w,
|
|
|
|
+ }
|
|
|
|
+ logging.file[sev] = rb
|
|
|
|
+}
|
|
|
|
+
|
|
// output writes the data to the log files and releases the buffer.
|
|
// output writes the data to the log files and releases the buffer.
|
|
func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
|
|
func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
|
|
l.mu.Lock()
|
|
l.mu.Lock()
|
|
@@ -676,33 +771,44 @@ func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoTo
|
|
}
|
|
}
|
|
}
|
|
}
|
|
data := buf.Bytes()
|
|
data := buf.Bytes()
|
|
- if !flag.Parsed() {
|
|
|
|
- os.Stderr.Write([]byte("ERROR: logging before flag.Parse: "))
|
|
|
|
- os.Stderr.Write(data)
|
|
|
|
- } else if l.toStderr {
|
|
|
|
|
|
+ if l.toStderr {
|
|
os.Stderr.Write(data)
|
|
os.Stderr.Write(data)
|
|
} else {
|
|
} else {
|
|
if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
|
|
if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
|
|
os.Stderr.Write(data)
|
|
os.Stderr.Write(data)
|
|
}
|
|
}
|
|
- if l.file[s] == nil {
|
|
|
|
- if err := l.createFiles(s); err != nil {
|
|
|
|
- os.Stderr.Write(data) // Make sure the message appears somewhere.
|
|
|
|
- l.exit(err)
|
|
|
|
|
|
+
|
|
|
|
+ if logging.logFile != "" {
|
|
|
|
+ // Since we are using a single log file, all of the items in l.file array
|
|
|
|
+ // will point to the same file, so just use one of them to write data.
|
|
|
|
+ if l.file[infoLog] == nil {
|
|
|
|
+ if err := l.createFiles(infoLog); err != nil {
|
|
|
|
+ os.Stderr.Write(data) // Make sure the message appears somewhere.
|
|
|
|
+ l.exit(err)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
|
|
- switch s {
|
|
|
|
- case fatalLog:
|
|
|
|
- l.file[fatalLog].Write(data)
|
|
|
|
- fallthrough
|
|
|
|
- case errorLog:
|
|
|
|
- l.file[errorLog].Write(data)
|
|
|
|
- fallthrough
|
|
|
|
- case warningLog:
|
|
|
|
- l.file[warningLog].Write(data)
|
|
|
|
- fallthrough
|
|
|
|
- case infoLog:
|
|
|
|
l.file[infoLog].Write(data)
|
|
l.file[infoLog].Write(data)
|
|
|
|
+ } else {
|
|
|
|
+ if l.file[s] == nil {
|
|
|
|
+ if err := l.createFiles(s); err != nil {
|
|
|
|
+ os.Stderr.Write(data) // Make sure the message appears somewhere.
|
|
|
|
+ l.exit(err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch s {
|
|
|
|
+ case fatalLog:
|
|
|
|
+ l.file[fatalLog].Write(data)
|
|
|
|
+ fallthrough
|
|
|
|
+ case errorLog:
|
|
|
|
+ l.file[errorLog].Write(data)
|
|
|
|
+ fallthrough
|
|
|
|
+ case warningLog:
|
|
|
|
+ l.file[warningLog].Write(data)
|
|
|
|
+ fallthrough
|
|
|
|
+ case infoLog:
|
|
|
|
+ l.file[infoLog].Write(data)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if s == fatalLog {
|
|
if s == fatalLog {
|
|
@@ -741,7 +847,7 @@ func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoTo
|
|
|
|
|
|
// timeoutFlush calls Flush and returns when it completes or after timeout
|
|
// timeoutFlush calls Flush and returns when it completes or after timeout
|
|
// elapses, whichever happens first. This is needed because the hooks invoked
|
|
// elapses, whichever happens first. This is needed because the hooks invoked
|
|
-// by Flush may deadlock when glog.Fatal is called from a hook that holds
|
|
|
|
|
|
+// by Flush may deadlock when klog.Fatal is called from a hook that holds
|
|
// a lock.
|
|
// a lock.
|
|
func timeoutFlush(timeout time.Duration) {
|
|
func timeoutFlush(timeout time.Duration) {
|
|
done := make(chan bool, 1)
|
|
done := make(chan bool, 1)
|
|
@@ -752,7 +858,7 @@ func timeoutFlush(timeout time.Duration) {
|
|
select {
|
|
select {
|
|
case <-done:
|
|
case <-done:
|
|
case <-time.After(timeout):
|
|
case <-time.After(timeout):
|
|
- fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout)
|
|
|
|
|
|
+ fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -802,18 +908,33 @@ func (l *loggingT) exit(err error) {
|
|
type syncBuffer struct {
|
|
type syncBuffer struct {
|
|
logger *loggingT
|
|
logger *loggingT
|
|
*bufio.Writer
|
|
*bufio.Writer
|
|
- file *os.File
|
|
|
|
- sev severity
|
|
|
|
- nbytes uint64 // The number of bytes written to this file
|
|
|
|
|
|
+ file *os.File
|
|
|
|
+ sev severity
|
|
|
|
+ nbytes uint64 // The number of bytes written to this file
|
|
|
|
+ maxbytes uint64 // The max number of bytes this syncBuffer.file can hold before cleaning up.
|
|
}
|
|
}
|
|
|
|
|
|
func (sb *syncBuffer) Sync() error {
|
|
func (sb *syncBuffer) Sync() error {
|
|
return sb.file.Sync()
|
|
return sb.file.Sync()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// CalculateMaxSize returns the real max size in bytes after considering the default max size and the flag options.
|
|
|
|
+func CalculateMaxSize() uint64 {
|
|
|
|
+ if logging.logFile != "" {
|
|
|
|
+ if logging.logFileMaxSizeMB == 0 {
|
|
|
|
+ // If logFileMaxSizeMB is zero, we don't have limitations on the log size.
|
|
|
|
+ return math.MaxUint64
|
|
|
|
+ }
|
|
|
|
+ // Flag logFileMaxSizeMB is in MB for user convenience.
|
|
|
|
+ return logging.logFileMaxSizeMB * 1024 * 1024
|
|
|
|
+ }
|
|
|
|
+ // If "log_file" flag is not specified, the target file (sb.file) will be cleaned up when reaches a fixed size.
|
|
|
|
+ return MaxSize
|
|
|
|
+}
|
|
|
|
+
|
|
func (sb *syncBuffer) Write(p []byte) (n int, err error) {
|
|
func (sb *syncBuffer) Write(p []byte) (n int, err error) {
|
|
- if sb.nbytes+uint64(len(p)) >= MaxSize {
|
|
|
|
- if err := sb.rotateFile(time.Now()); err != nil {
|
|
|
|
|
|
+ if sb.nbytes+uint64(len(p)) >= sb.maxbytes {
|
|
|
|
+ if err := sb.rotateFile(time.Now(), false); err != nil {
|
|
sb.logger.exit(err)
|
|
sb.logger.exit(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -826,13 +947,15 @@ func (sb *syncBuffer) Write(p []byte) (n int, err error) {
|
|
}
|
|
}
|
|
|
|
|
|
// rotateFile closes the syncBuffer's file and starts a new one.
|
|
// rotateFile closes the syncBuffer's file and starts a new one.
|
|
-func (sb *syncBuffer) rotateFile(now time.Time) error {
|
|
|
|
|
|
+// The startup argument indicates whether this is the initial startup of klog.
|
|
|
|
+// If startup is true, existing files are opened for appending instead of truncated.
|
|
|
|
+func (sb *syncBuffer) rotateFile(now time.Time, startup bool) error {
|
|
if sb.file != nil {
|
|
if sb.file != nil {
|
|
sb.Flush()
|
|
sb.Flush()
|
|
sb.file.Close()
|
|
sb.file.Close()
|
|
}
|
|
}
|
|
var err error
|
|
var err error
|
|
- sb.file, _, err = create(severityName[sb.sev], now)
|
|
|
|
|
|
+ sb.file, _, err = create(severityName[sb.sev], now, startup)
|
|
sb.nbytes = 0
|
|
sb.nbytes = 0
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
@@ -840,6 +963,10 @@ func (sb *syncBuffer) rotateFile(now time.Time) error {
|
|
|
|
|
|
sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
|
|
sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
|
|
|
|
|
|
|
|
+ if sb.logger.skipLogHeaders {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
// Write header.
|
|
// Write header.
|
|
var buf bytes.Buffer
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
|
|
fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
|
|
@@ -864,10 +991,11 @@ func (l *loggingT) createFiles(sev severity) error {
|
|
// has already been created, we can stop.
|
|
// has already been created, we can stop.
|
|
for s := sev; s >= infoLog && l.file[s] == nil; s-- {
|
|
for s := sev; s >= infoLog && l.file[s] == nil; s-- {
|
|
sb := &syncBuffer{
|
|
sb := &syncBuffer{
|
|
- logger: l,
|
|
|
|
- sev: s,
|
|
|
|
|
|
+ logger: l,
|
|
|
|
+ sev: s,
|
|
|
|
+ maxbytes: CalculateMaxSize(),
|
|
}
|
|
}
|
|
- if err := sb.rotateFile(now); err != nil {
|
|
|
|
|
|
+ if err := sb.rotateFile(now, true); err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
l.file[s] = sb
|
|
l.file[s] = sb
|
|
@@ -875,11 +1003,11 @@ func (l *loggingT) createFiles(sev severity) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-const flushInterval = 30 * time.Second
|
|
|
|
|
|
+const flushInterval = 5 * time.Second
|
|
|
|
|
|
// flushDaemon periodically flushes the log file buffers.
|
|
// flushDaemon periodically flushes the log file buffers.
|
|
func (l *loggingT) flushDaemon() {
|
|
func (l *loggingT) flushDaemon() {
|
|
- for _ = range time.NewTicker(flushInterval).C {
|
|
|
|
|
|
+ for range time.NewTicker(flushInterval).C {
|
|
l.lockAndFlushAll()
|
|
l.lockAndFlushAll()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -986,9 +1114,9 @@ type Verbose bool
|
|
// The returned value is a boolean of type Verbose, which implements Info, Infoln
|
|
// The returned value is a boolean of type Verbose, which implements Info, Infoln
|
|
// and Infof. These methods will write to the Info log if called.
|
|
// and Infof. These methods will write to the Info log if called.
|
|
// Thus, one may write either
|
|
// Thus, one may write either
|
|
-// if glog.V(2) { glog.Info("log this") }
|
|
|
|
|
|
+// if klog.V(2) { klog.Info("log this") }
|
|
// or
|
|
// or
|
|
-// glog.V(2).Info("log this")
|
|
|
|
|
|
+// klog.V(2).Info("log this")
|
|
// The second form is shorter but the first is cheaper if logging is off because it does
|
|
// The second form is shorter but the first is cheaper if logging is off because it does
|
|
// not evaluate its arguments.
|
|
// not evaluate its arguments.
|
|
//
|
|
//
|
|
@@ -1062,7 +1190,7 @@ func InfoDepth(depth int, args ...interface{}) {
|
|
}
|
|
}
|
|
|
|
|
|
// Infoln logs to the INFO log.
|
|
// Infoln logs to the INFO log.
|
|
-// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
|
|
|
|
|
|
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
|
|
func Infoln(args ...interface{}) {
|
|
func Infoln(args ...interface{}) {
|
|
logging.println(infoLog, args...)
|
|
logging.println(infoLog, args...)
|
|
}
|
|
}
|
|
@@ -1086,7 +1214,7 @@ func WarningDepth(depth int, args ...interface{}) {
|
|
}
|
|
}
|
|
|
|
|
|
// Warningln logs to the WARNING and INFO logs.
|
|
// Warningln logs to the WARNING and INFO logs.
|
|
-// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
|
|
|
|
|
|
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
|
|
func Warningln(args ...interface{}) {
|
|
func Warningln(args ...interface{}) {
|
|
logging.println(warningLog, args...)
|
|
logging.println(warningLog, args...)
|
|
}
|
|
}
|
|
@@ -1110,7 +1238,7 @@ func ErrorDepth(depth int, args ...interface{}) {
|
|
}
|
|
}
|
|
|
|
|
|
// Errorln logs to the ERROR, WARNING, and INFO logs.
|
|
// Errorln logs to the ERROR, WARNING, and INFO logs.
|
|
-// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
|
|
|
|
|
|
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
|
|
func Errorln(args ...interface{}) {
|
|
func Errorln(args ...interface{}) {
|
|
logging.println(errorLog, args...)
|
|
logging.println(errorLog, args...)
|
|
}
|
|
}
|
|
@@ -1136,7 +1264,7 @@ func FatalDepth(depth int, args ...interface{}) {
|
|
|
|
|
|
// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
|
|
// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
|
|
// including a stack trace of all running goroutines, then calls os.Exit(255).
|
|
// including a stack trace of all running goroutines, then calls os.Exit(255).
|
|
-// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
|
|
|
|
|
|
+// Arguments are handled in the manner of fmt.Println; a newline is always appended.
|
|
func Fatalln(args ...interface{}) {
|
|
func Fatalln(args ...interface{}) {
|
|
logging.println(fatalLog, args...)
|
|
logging.println(fatalLog, args...)
|
|
}
|
|
}
|