diff --git a/staging/src/k8s.io/component-base/logs/json/json.go b/staging/src/k8s.io/component-base/logs/json/json.go index 1adada10cd4..a39a60dcd05 100644 --- a/staging/src/k8s.io/component-base/logs/json/json.go +++ b/staging/src/k8s.io/component-base/logs/json/json.go @@ -17,6 +17,7 @@ limitations under the License. package logs import ( + "io" "os" "time" @@ -83,9 +84,17 @@ type Factory struct{} var _ registry.LogFormatFactory = Factory{} func (f Factory) Create(options config.FormatOptions) (logr.Logger, func()) { - stderr := zapcore.Lock(os.Stderr) + // We intentionally avoid all os.File.Sync calls. Output is unbuffered, + // therefore we don't need to flush, and calling the underlying fsync + // would just slow down writing. + // + // The assumption is that logging only needs to ensure that data gets + // written to the output stream before the process terminates, but + // doesn't need to worry about data not being written because of a + // system crash or powerloss. + stderr := zapcore.Lock(AddNopSync(os.Stderr)) if options.JSON.SplitStream { - stdout := zapcore.Lock(os.Stdout) + stdout := zapcore.Lock(AddNopSync(os.Stdout)) size := options.JSON.InfoBufferSize.Value() if size > 0 { // Prevent integer overflow. @@ -103,3 +112,16 @@ func (f Factory) Create(options config.FormatOptions) (logr.Logger, func()) { // Write info messages and errors to stderr to prevent mixing with normal program output. return NewJSONLogger(stderr, nil, nil) } + +// AddNoSync adds a NOP Sync implementation. +func AddNopSync(writer io.Writer) zapcore.WriteSyncer { + return nopSync{Writer: writer} +} + +type nopSync struct { + io.Writer +} + +func (f nopSync) Sync() error { + return nil +} diff --git a/test/integration/logs/benchmark/benchmark_test.go b/test/integration/logs/benchmark/benchmark_test.go index 2312a485c4c..e810e88f3a3 100644 --- a/test/integration/logs/benchmark/benchmark_test.go +++ b/test/integration/logs/benchmark/benchmark_test.go @@ -33,7 +33,6 @@ import ( "time" "github.com/go-logr/logr" - "go.uber.org/zap/zapcore" "k8s.io/component-base/logs" logsjson "k8s.io/component-base/logs/json" "k8s.io/klog/v2" @@ -173,10 +172,11 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo klog.SetOutput(out) defer klog.SetOutput(&output) } - generateOutput(b, config, out) + generateOutput(b, config, nil, out) }) b.Run("JSON", func(b *testing.B) { var logger logr.Logger + var flush func() var out1, out2 *os.File if !discard { var err error @@ -193,7 +193,7 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo } b.Run("single-stream", func(b *testing.B) { if discard { - logger, _ = logsjson.NewJSONLogger(zapcore.AddSync(&output), nil, nil) + logger, flush = logsjson.NewJSONLogger(logsjson.AddNopSync(&output), nil, nil) } else { stderr := os.Stderr os.Stderr = out1 @@ -201,16 +201,16 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo os.Stderr = stderr }() options := logs.NewOptions() - logger, _ = logsjson.Factory{}.Create(options.Config.Options) + logger, flush = logsjson.Factory{}.Create(options.Config.Options) } klog.SetLogger(logger) defer klog.ClearLogger() - generateOutput(b, config, out1) + generateOutput(b, config, flush, out1) }) b.Run("split-stream", func(b *testing.B) { if discard { - logger, _ = logsjson.NewJSONLogger(zapcore.AddSync(&output), zapcore.AddSync(&output), nil) + logger, flush = logsjson.NewJSONLogger(logsjson.AddNopSync(&output), logsjson.AddNopSync(&output), nil) } else { stdout, stderr := os.Stdout, os.Stderr os.Stdout, os.Stderr = out1, out2 @@ -219,16 +219,16 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo }() options := logs.NewOptions() options.Config.Options.JSON.SplitStream = true - logger, _ = logsjson.Factory{}.Create(options.Config.Options) + logger, flush = logsjson.Factory{}.Create(options.Config.Options) } klog.SetLogger(logger) defer klog.ClearLogger() - generateOutput(b, config, out1, out2) + generateOutput(b, config, flush, out1, out2) }) }) } -func generateOutput(b *testing.B, config loadGeneratorConfig, files ...*os.File) { +func generateOutput(b *testing.B, config loadGeneratorConfig, flush func(), files ...*os.File) { msg := strings.Repeat("X", config.messageLength) err := errors.New("fail") start := time.Now() @@ -257,6 +257,9 @@ func generateOutput(b *testing.B, config loadGeneratorConfig, files ...*os.File) } wg.Wait() klog.Flush() + if flush != nil { + flush() + } b.StopTimer() // Print some information about the result.