Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a log-file-max-size flag to fix large logfile (>1.8GB) truncation. #56

Merged
merged 1 commit into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions klog.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import (
"fmt"
"io"
stdLog "log"
"math"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -410,6 +411,9 @@ func InitFlags(flagset *flag.FlagSet) {
}
flagset.StringVar(&logging.logDir, "log_dir", "", "If non-empty, write log files in this directory")
flagset.StringVar(&logging.logFile, "log_file", "", "If non-empty, use this log file")
flagset.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", 1800,
"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", true, "log to standard error instead of files")
flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
flagset.Var(&logging.verbosity, "v", "number for the log level verbosity")
Expand Down Expand Up @@ -472,6 +476,10 @@ type loggingT struct {
// 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

Expand Down Expand Up @@ -865,17 +873,32 @@ func (l *loggingT) exit(err error) {
type syncBuffer struct {
logger *loggingT
*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 {
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
}
yuwenma marked this conversation as resolved.
Show resolved Hide resolved

func (sb *syncBuffer) Write(p []byte) (n int, err error) {
if sb.nbytes+uint64(len(p)) >= MaxSize {
if sb.nbytes+uint64(len(p)) >= sb.maxbytes {
if err := sb.rotateFile(time.Now(), false); err != nil {
sb.logger.exit(err)
}
Expand All @@ -890,7 +913,7 @@ func (sb *syncBuffer) Write(p []byte) (n int, err error) {

// rotateFile closes the syncBuffer's file and starts a new one.
// The startup argument indicates whether this is the initial startup of klog.
// If startup is true, existing files are opened for apending instead of truncated.
// 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 {
sb.Flush()
Expand Down Expand Up @@ -933,8 +956,9 @@ func (l *loggingT) createFiles(sev severity) error {
// has already been created, we can stop.
for s := sev; s >= infoLog && l.file[s] == nil; s-- {
sb := &syncBuffer{
logger: l,
sev: s,
logger: l,
sev: s,
maxbytes: CalculateMaxSize(),
}
if err := sb.rotateFile(now, true); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion klog_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ var onceLogDirs sync.Once
// successfully, create also attempts to update the symlink for that tag, ignoring
// errors.
// The startup argument indicates whether this is the initial startup of klog.
// If startup is true, existing files are opened for apending instead of truncated.
// If startup is true, existing files are opened for appending instead of truncated.
func create(tag string, t time.Time, startup bool) (f *os.File, filename string, err error) {
if logging.logFile != "" {
f, err := openOrCreate(logging.logFile, startup)
Expand Down
50 changes: 48 additions & 2 deletions klog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,6 @@ func TestRollover(t *testing.T) {
}
defer func(previous uint64) { MaxSize = previous }(MaxSize)
MaxSize = 512

Info("x") // Be sure we have a file.
info, ok := logging.file[infoLog].(*syncBuffer)
if !ok {
Expand Down Expand Up @@ -370,7 +369,7 @@ func TestRollover(t *testing.T) {
if fname0 == fname1 {
t.Errorf("info.f.Name did not change: %v", fname0)
}
if info.nbytes >= MaxSize {
if info.nbytes >= info.maxbytes {
t.Errorf("file size was not reset: %d", info.nbytes)
}
}
Expand Down Expand Up @@ -487,3 +486,50 @@ func BenchmarkHeader(b *testing.B) {
logging.putBuffer(buf)
}
}

// Test the logic on checking log size limitation.
func TestFileSizeCheck(t *testing.T) {
setFlags()
testData := map[string]struct {
testLogFile string
testLogFileMaxSizeMB uint64
testCurrentSize uint64
expectedResult bool
}{
"logFile not specified, exceeds max size": {
testLogFile: "",
testLogFileMaxSizeMB: 1,
testCurrentSize: 1024 * 1024 * 2000, //exceeds the maxSize
expectedResult: true,
},

"logFile not specified, not exceeds max size": {
testLogFile: "",
testLogFileMaxSizeMB: 1,
testCurrentSize: 1024 * 1024 * 1000, //smaller than the maxSize
expectedResult: false,
},
"logFile specified, exceeds max size": {
testLogFile: "/tmp/test.log",
testLogFileMaxSizeMB: 500, // 500MB
testCurrentSize: 1024 * 1024 * 1000, //exceeds the logFileMaxSizeMB
expectedResult: true,
},
"logFile specified, not exceeds max size": {
testLogFile: "/tmp/test.log",
testLogFileMaxSizeMB: 500, // 500MB
testCurrentSize: 1024 * 1024 * 300, //smaller than the logFileMaxSizeMB
expectedResult: false,
},
}

for name, test := range testData {
logging.logFile = test.testLogFile
logging.logFileMaxSizeMB = test.testLogFileMaxSizeMB
actualResult := test.testCurrentSize >= CalculateMaxSize()
if test.expectedResult != actualResult {
t.Fatalf("Error on test case '%v': Was expecting result equals %v, got %v",
name, test.expectedResult, actualResult)
}
}
}