Skip to content

Commit 803424c

Browse files
committed
feat: make logger more dynamic by allowing synchronous lumberjack
feat: make logger more dynamic by allowing synchronous lumberjack feat: make logger more dynamic by allowing synchronous lumberjack
1 parent 3a74795 commit 803424c

5 files changed

Lines changed: 96 additions & 46 deletions

File tree

cmd/base/options/log.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,24 @@ import (
2424
)
2525

2626
type LogsOptions struct {
27-
LogPackageLevel general.LoggingPKG
28-
LogFileMaxSizeInMB uint64
29-
SupportAsyncLogging bool
30-
LogDir string
31-
LogBufferSize int
32-
LogFileMaxAge int
33-
LogFileMaxBackups int
27+
LogPackageLevel general.LoggingPKG
28+
LogFileMaxSizeInMB uint64
29+
CustomLogDir string
30+
LogBufferSize int
31+
LogFileMaxAge int
32+
LogFileMaxBackups int
3433
}
3534

3635
func NewLogsOptions() *LogsOptions {
3736
return &LogsOptions{
3837
LogPackageLevel: general.LoggingPKGFull,
3938
LogFileMaxSizeInMB: 1800,
40-
LogBufferSize: 10000,
41-
LogFileMaxAge: 7,
42-
LogFileMaxBackups: 10,
4339
}
4440
}
4541

4642
// AddFlags adds flags to the specified FlagSet.
4743
func (o *LogsOptions) AddFlags(fs *pflag.FlagSet) {
48-
fs.BoolVar(&o.SupportAsyncLogging, "support-async-logging", o.SupportAsyncLogging, "whether to support async logging")
49-
fs.StringVar(&o.LogDir, "async_log_dir", o.LogDir, "directory to store logs")
44+
fs.StringVar(&o.CustomLogDir, "custom_log_dir", o.CustomLogDir, "directory to store logs for custom logger")
5045
fs.Var(&o.LogPackageLevel, "logs-package-level", "the default package level for logging")
5146
fs.Uint64Var(&o.LogFileMaxSizeInMB, "log-file-max-size", o.LogFileMaxSizeInMB, "Max size of klog file in MB.")
5247
fs.IntVar(&o.LogBufferSize, "log-buffer-size", o.LogBufferSize, "size of the ring buffer to store async logs")
@@ -57,8 +52,7 @@ func (o *LogsOptions) AddFlags(fs *pflag.FlagSet) {
5752
func (o *LogsOptions) ApplyTo(c *generic.LogConfiguration) error {
5853
general.SetDefaultLoggingPackage(o.LogPackageLevel)
5954
general.SetLogFileMaxSize(o.LogFileMaxSizeInMB)
60-
c.SupportAsyncLogging = o.SupportAsyncLogging
61-
c.LogDir = o.LogDir
55+
c.CustomLogDir = o.CustomLogDir
6256
c.LogBufferSize = o.LogBufferSize
6357
c.LogFileMaxSize = int(o.LogFileMaxSizeInMB)
6458
c.LogFileMaxAge = o.LogFileMaxAge

cmd/katalyst-agent/app/agent.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ func Run(
6363
return err
6464
}
6565

66-
if conf.SupportAsyncLogging {
67-
asyncLogger := logging.NewAsyncLogger(genericCtx, conf.LogDir, conf.LogFileMaxSize, conf.LogFileMaxAge, conf.LogFileMaxBackups, conf.LogBufferSize)
68-
defer asyncLogger.Shutdown()
69-
}
66+
asyncLogger := logging.NewCustomLogger(genericCtx, conf.CustomLogDir, conf.LogFileMaxSize, conf.LogFileMaxAge, conf.LogFileMaxBackups, conf.LogBufferSize)
67+
defer asyncLogger.Shutdown()
7068

7169
for _, genericOption := range genericOptions {
7270
genericOption(genericCtx)

pkg/config/generic/log.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ limitations under the License.
1717
package generic
1818

1919
type LogConfiguration struct {
20-
SupportAsyncLogging bool
21-
LogDir string
22-
LogFileMaxSize int
23-
LogBufferSize int
24-
LogFileMaxAge int
25-
LogFileMaxBackups int
20+
CustomLogDir string
21+
LogFileMaxSize int
22+
LogBufferSize int
23+
LogFileMaxAge int
24+
LogFileMaxBackups int
2625
}
2726

2827
func NewLogConfiguration() *LogConfiguration {

pkg/util/logging/logger.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,22 @@ var logInfoMap = map[SeverityName]*logInfo{
6767
FatalSeverity: {fileName: defaultFatalLogFileName, metricsName: metricsNameNumDroppedFatalLogs},
6868
}
6969

70-
type AsyncLogger struct {
70+
type CustomLogger struct {
7171
diodeWriters []diode.Writer
7272
}
7373

74-
// NewAsyncLogger creates an async logger that produces an async writer for each of the severity levels.
75-
// The async writer spins up a goroutine that periodically flushes the buffered logs to disk.
76-
func NewAsyncLogger(
74+
// NewCustomLogger creates a custom logger that can either be asynchronous or synchronous, depending on configuration.
75+
func NewCustomLogger(
7776
agentCtx *agent.GenericContext, logDir string, maxSizeMB, maxAge, maxBackups, bufferSize int,
78-
) *AsyncLogger {
77+
) *CustomLogger {
7978
wrappedEmitter := agentCtx.EmitterPool.GetDefaultMetricsEmitter()
8079

81-
asyncLogger := &AsyncLogger{}
80+
// If logDir is not set, we are still using klog's native logger, so we just return an empty logger without calling SetOutput()
81+
if logDir == "" {
82+
return &CustomLogger{}
83+
}
84+
85+
customLogger := &CustomLogger{}
8286
for severity, logInfo := range logInfoMap {
8387
// lumberjackLogger is a logger that rotates log files
8488
lumberjackLogger := &lumberjack.Logger{
@@ -88,19 +92,26 @@ func NewAsyncLogger(
8892
MaxBackups: maxBackups,
8993
}
9094

91-
// diodeWriter is a writer that stores logs in a ring buffer and asynchronously flushes them
92-
diodeWriter := diode.NewWriter(lumberjackLogger, bufferSize, 10*time.Millisecond, func(missed int) {
93-
_ = wrappedEmitter.StoreInt64(logInfo.metricsName, int64(missed), metrics.MetricTypeNameRaw)
94-
})
95-
// Overrides the default synchronous writer with the diode writer
96-
klog.SetOutputBySeverity(string(severity), diodeWriter)
97-
asyncLogger.diodeWriters = append(asyncLogger.diodeWriters, diodeWriter)
95+
// Enable async logger if buffer size is more than 0; otherwise, use synchronous lumberjack logger
96+
if bufferSize > 0 {
97+
// diodeWriter is a writer that stores logs in a ring buffer and asynchronously flushes them to disk
98+
diodeWriter := diode.NewWriter(lumberjackLogger, bufferSize, 10*time.Millisecond, func(missed int) {
99+
_ = wrappedEmitter.StoreInt64(logInfo.metricsName, int64(missed), metrics.MetricTypeNameRaw)
100+
})
101+
// Overrides the default synchronous writer with the diode writer
102+
klog.SetOutputBySeverity(string(severity), diodeWriter)
103+
customLogger.diodeWriters = append(customLogger.diodeWriters, diodeWriter)
104+
klog.Infof("custom async logger is enabled for the severity %s", severity)
105+
} else {
106+
klog.SetOutputBySeverity(string(severity), lumberjackLogger)
107+
klog.Infof("custom sync logger is enabled for the severity %s", severity)
108+
}
98109
}
99110

100-
return asyncLogger
111+
return customLogger
101112
}
102113

103-
func (a *AsyncLogger) Shutdown() {
114+
func (a *CustomLogger) Shutdown() {
104115
klog.Info("[Shutdown] async writer is shutting down...")
105116
klog.Flush()
106117
for _, writer := range a.diodeWriters {

pkg/util/logging/logger_test.go

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,63 @@ import (
2626
metrics_pool "github.com/kubewharf/katalyst-core/pkg/metrics/metrics-pool"
2727
)
2828

29-
func TestNewAsyncLogger(t *testing.T) {
29+
func TestNewCustomLogger(t *testing.T) {
3030
t.Parallel()
31-
agentCtx := &agent.GenericContext{
32-
GenericContext: &katalyst_base.GenericContext{
33-
EmitterPool: metrics_pool.DummyMetricsEmitterPool{},
31+
testCases := []struct {
32+
name string
33+
logDir string
34+
maxSizeMB int
35+
maxAge int
36+
maxBackups int
37+
bufferSize int
38+
expectedDiodeWritersCount int
39+
}{
40+
{
41+
name: "Disabled logger (logDir is empty), logger is nil",
42+
logDir: "",
43+
maxSizeMB: 10,
44+
maxAge: 0,
45+
maxBackups: 10,
46+
bufferSize: 1024,
47+
expectedDiodeWritersCount: 0,
48+
},
49+
{
50+
name: "Synchronous logger (bufferSize=0)",
51+
logDir: t.TempDir(),
52+
maxSizeMB: 1,
53+
maxAge: 1,
54+
maxBackups: 1,
55+
bufferSize: 0,
56+
expectedDiodeWritersCount: 0,
57+
},
58+
{
59+
name: "Asynchronous logger",
60+
logDir: t.TempDir(),
61+
maxSizeMB: 1,
62+
maxAge: 1,
63+
maxBackups: 1,
64+
bufferSize: 1024,
65+
expectedDiodeWritersCount: 4,
3466
},
3567
}
36-
asyncLogger := NewAsyncLogger(agentCtx, "testDir", 100, 100, 100, 100)
37-
assert.NotNil(t, asyncLogger)
3868

39-
assert.Equal(t, len(asyncLogger.diodeWriters), 4)
69+
for _, tc := range testCases {
70+
tc := tc
71+
t.Run(tc.name, func(t *testing.T) {
72+
t.Parallel()
73+
agentCtx := &agent.GenericContext{
74+
GenericContext: &katalyst_base.GenericContext{
75+
EmitterPool: metrics_pool.DummyMetricsEmitterPool{},
76+
},
77+
}
78+
79+
logger := NewCustomLogger(agentCtx, tc.logDir, tc.maxSizeMB, tc.maxAge, tc.maxBackups, tc.bufferSize)
80+
81+
assert.NotNil(t, logger)
82+
assert.Len(t, logger.diodeWriters, tc.expectedDiodeWritersCount)
83+
assert.NotPanics(t, func() {
84+
logger.Shutdown()
85+
})
86+
})
87+
}
4088
}

0 commit comments

Comments
 (0)