e3.1.2 Changed output stdout to console for clarity.
This commit is contained in:
122
builder.go
122
builder.go
@ -94,12 +94,6 @@ func (b *Builder) MaxSizeMB(size int64) *Builder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableStdout enables mirroring logs to stdout/stderr.
|
|
||||||
func (b *Builder) EnableStdout(enable bool) *Builder {
|
|
||||||
b.cfg.EnableStdout = enable
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableFile disables file output entirely.
|
// DisableFile disables file output entirely.
|
||||||
func (b *Builder) DisableFile(disable bool) *Builder {
|
func (b *Builder) DisableFile(disable bool) *Builder {
|
||||||
b.cfg.DisableFile = disable
|
b.cfg.DisableFile = disable
|
||||||
@ -118,6 +112,120 @@ func (b *Builder) HeartbeatIntervalS(interval int64) *Builder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowTimestamp sets whether to show timestamps in logs.
|
||||||
|
func (b *Builder) ShowTimestamp(show bool) *Builder {
|
||||||
|
b.cfg.ShowTimestamp = show
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowLevel sets whether to show log levels.
|
||||||
|
func (b *Builder) ShowLevel(show bool) *Builder {
|
||||||
|
b.cfg.ShowLevel = show
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimestampFormat sets the timestamp format string.
|
||||||
|
func (b *Builder) TimestampFormat(format string) *Builder {
|
||||||
|
b.cfg.TimestampFormat = format
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxTotalSizeKB sets the maximum total size of all log files in KB.
|
||||||
|
func (b *Builder) MaxTotalSizeKB(size int64) *Builder {
|
||||||
|
b.cfg.MaxTotalSizeKB = size
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxTotalSizeMB sets the maximum total size of all log files in MB. Convenience.
|
||||||
|
func (b *Builder) MaxTotalSizeMB(size int64) *Builder {
|
||||||
|
b.cfg.MaxTotalSizeKB = size * 1000
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinDiskFreeKB sets the minimum required free disk space in KB.
|
||||||
|
func (b *Builder) MinDiskFreeKB(size int64) *Builder {
|
||||||
|
b.cfg.MinDiskFreeKB = size
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinDiskFreeMB sets the minimum required free disk space in MB. Convenience.
|
||||||
|
func (b *Builder) MinDiskFreeMB(size int64) *Builder {
|
||||||
|
b.cfg.MinDiskFreeKB = size * 1000
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushIntervalMs sets the flush interval in milliseconds.
|
||||||
|
func (b *Builder) FlushIntervalMs(interval int64) *Builder {
|
||||||
|
b.cfg.FlushIntervalMs = interval
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraceDepth sets the default trace depth for stack traces.
|
||||||
|
func (b *Builder) TraceDepth(depth int64) *Builder {
|
||||||
|
b.cfg.TraceDepth = depth
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetentionPeriodHrs sets the log retention period in hours.
|
||||||
|
func (b *Builder) RetentionPeriodHrs(hours float64) *Builder {
|
||||||
|
b.cfg.RetentionPeriodHrs = hours
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetentionCheckMins sets the retention check interval in minutes.
|
||||||
|
func (b *Builder) RetentionCheckMins(mins float64) *Builder {
|
||||||
|
b.cfg.RetentionCheckMins = mins
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskCheckIntervalMs sets the disk check interval in milliseconds.
|
||||||
|
func (b *Builder) DiskCheckIntervalMs(interval int64) *Builder {
|
||||||
|
b.cfg.DiskCheckIntervalMs = interval
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableAdaptiveInterval enables adaptive disk check intervals.
|
||||||
|
func (b *Builder) EnableAdaptiveInterval(enable bool) *Builder {
|
||||||
|
b.cfg.EnableAdaptiveInterval = enable
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnablePeriodicSync enables periodic file sync.
|
||||||
|
func (b *Builder) EnablePeriodicSync(enable bool) *Builder {
|
||||||
|
b.cfg.EnablePeriodicSync = enable
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinCheckIntervalMs sets the minimum disk check interval in milliseconds.
|
||||||
|
func (b *Builder) MinCheckIntervalMs(interval int64) *Builder {
|
||||||
|
b.cfg.MinCheckIntervalMs = interval
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxCheckIntervalMs sets the maximum disk check interval in milliseconds.
|
||||||
|
func (b *Builder) MaxCheckIntervalMs(interval int64) *Builder {
|
||||||
|
b.cfg.MaxCheckIntervalMs = interval
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsoleTarget sets the console output target ("stdout", "stderr", or "split").
|
||||||
|
func (b *Builder) ConsoleTarget(target string) *Builder {
|
||||||
|
b.cfg.ConsoleTarget = target
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalErrorsToStderr sets whether to write internal errors to stderr.
|
||||||
|
func (b *Builder) InternalErrorsToStderr(enable bool) *Builder {
|
||||||
|
b.cfg.InternalErrorsToStderr = enable
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableConsole enables mirroring logs to console.
|
||||||
|
func (b *Builder) EnableConsole(enable bool) *Builder {
|
||||||
|
b.cfg.EnableConsole = enable
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// Example usage:
|
// Example usage:
|
||||||
// logger, err := log.NewBuilder().
|
// logger, err := log.NewBuilder().
|
||||||
//
|
//
|
||||||
@ -125,7 +233,7 @@ func (b *Builder) HeartbeatIntervalS(interval int64) *Builder {
|
|||||||
// LevelString("debug").
|
// LevelString("debug").
|
||||||
// Format("json").
|
// Format("json").
|
||||||
// BufferSize(4096).
|
// BufferSize(4096).
|
||||||
// EnableStdout(true).
|
// EnableConsole(true).
|
||||||
// Build()
|
// Build()
|
||||||
//
|
//
|
||||||
// if err == nil {
|
// if err == nil {
|
||||||
|
|||||||
@ -20,7 +20,7 @@ func TestBuilder_Build(t *testing.T) {
|
|||||||
LevelString("debug").
|
LevelString("debug").
|
||||||
Format("json").
|
Format("json").
|
||||||
BufferSize(2048).
|
BufferSize(2048).
|
||||||
EnableStdout(true).
|
EnableConsole(true).
|
||||||
MaxSizeMB(10).
|
MaxSizeMB(10).
|
||||||
HeartbeatLevel(2).
|
HeartbeatLevel(2).
|
||||||
Build()
|
Build()
|
||||||
@ -43,7 +43,7 @@ func TestBuilder_Build(t *testing.T) {
|
|||||||
assert.Equal(t, LevelDebug, cfg.Level)
|
assert.Equal(t, LevelDebug, cfg.Level)
|
||||||
assert.Equal(t, "json", cfg.Format)
|
assert.Equal(t, "json", cfg.Format)
|
||||||
assert.Equal(t, int64(2048), cfg.BufferSize)
|
assert.Equal(t, int64(2048), cfg.BufferSize)
|
||||||
assert.True(t, cfg.EnableStdout, "EnableStdout should be true")
|
assert.True(t, cfg.EnableConsole, "EnableConsole should be true")
|
||||||
assert.Equal(t, int64(10*1000), cfg.MaxSizeKB)
|
assert.Equal(t, int64(10*1000), cfg.MaxSizeKB)
|
||||||
assert.Equal(t, int64(2), cfg.HeartbeatLevel)
|
assert.Equal(t, int64(2), cfg.HeartbeatLevel)
|
||||||
})
|
})
|
||||||
|
|||||||
24
config.go
24
config.go
@ -46,8 +46,8 @@ type Config struct {
|
|||||||
HeartbeatIntervalS int64 `toml:"heartbeat_interval_s"` // Interval seconds for heartbeat
|
HeartbeatIntervalS int64 `toml:"heartbeat_interval_s"` // Interval seconds for heartbeat
|
||||||
|
|
||||||
// Stdout/console output settings
|
// Stdout/console output settings
|
||||||
EnableStdout bool `toml:"enable_stdout"` // Mirror logs to stdout/stderr
|
EnableConsole bool `toml:"enable_console"` // Mirror logs to stdout/stderr
|
||||||
StdoutTarget string `toml:"stdout_target"` // "stdout" or "stderr"
|
ConsoleTarget string `toml:"console_target"` // "stdout" or "stderr"
|
||||||
DisableFile bool `toml:"disable_file"` // Disable file output entirely
|
DisableFile bool `toml:"disable_file"` // Disable file output entirely
|
||||||
|
|
||||||
// Internal error handling
|
// Internal error handling
|
||||||
@ -92,8 +92,8 @@ var defaultConfig = Config{
|
|||||||
HeartbeatIntervalS: 60,
|
HeartbeatIntervalS: 60,
|
||||||
|
|
||||||
// Stdout settings
|
// Stdout settings
|
||||||
EnableStdout: false,
|
EnableConsole: false,
|
||||||
StdoutTarget: "stdout",
|
ConsoleTarget: "stdout",
|
||||||
DisableFile: false,
|
DisableFile: false,
|
||||||
|
|
||||||
// Internal error handling
|
// Internal error handling
|
||||||
@ -131,8 +131,8 @@ func (c *Config) Validate() error {
|
|||||||
return fmtErrorf("timestamp_format cannot be empty")
|
return fmtErrorf("timestamp_format cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.StdoutTarget != "stdout" && c.StdoutTarget != "stderr" && c.StdoutTarget != "split" {
|
if c.ConsoleTarget != "stdout" && c.ConsoleTarget != "stderr" && c.ConsoleTarget != "split" {
|
||||||
return fmtErrorf("invalid stdout_target: '%s' (use stdout, stderr, or split)", c.StdoutTarget)
|
return fmtErrorf("invalid console_target: '%s' (use stdout, stderr, or split)", c.ConsoleTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Numeric validations
|
// Numeric validations
|
||||||
@ -315,15 +315,15 @@ func applyConfigField(cfg *Config, key, value string) error {
|
|||||||
}
|
}
|
||||||
cfg.HeartbeatIntervalS = intVal
|
cfg.HeartbeatIntervalS = intVal
|
||||||
|
|
||||||
// Stdout/console output settings
|
// Console output settings
|
||||||
case "enable_stdout":
|
case "enable_console":
|
||||||
boolVal, err := strconv.ParseBool(value)
|
boolVal, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmtErrorf("invalid boolean value for enable_stdout '%s': %w", value, err)
|
return fmtErrorf("invalid boolean value for enable_console '%s': %w", value, err)
|
||||||
}
|
}
|
||||||
cfg.EnableStdout = boolVal
|
cfg.EnableConsole = boolVal
|
||||||
case "stdout_target":
|
case "console_target":
|
||||||
cfg.StdoutTarget = value
|
cfg.ConsoleTarget = value
|
||||||
case "disable_file":
|
case "disable_file":
|
||||||
boolVal, err := strconv.ParseBool(value)
|
boolVal, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -84,8 +84,8 @@ func TestConfigValidate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid stdout target",
|
name: "invalid stdout target",
|
||||||
modify: func(c *Config) { c.StdoutTarget = "invalid" },
|
modify: func(c *Config) { c.ConsoleTarget = "invalid" },
|
||||||
wantError: "invalid stdout_target",
|
wantError: "invalid console_target",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "min > max check interval",
|
name: "min > max check interval",
|
||||||
|
|||||||
@ -286,7 +286,7 @@ builder := compat.NewBuilder().
|
|||||||
"format=txt", // Human-readable
|
"format=txt", // Human-readable
|
||||||
"level=-4", // Debug level
|
"level=-4", // Debug level
|
||||||
"trace_depth=3", // Include traces
|
"trace_depth=3", // Include traces
|
||||||
"enable_stdout=true", // Console output
|
"enable_console=true", // Console output
|
||||||
"flush_interval_ms=50", // Quick feedback
|
"flush_interval_ms=50", // Quick feedback
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -297,7 +297,7 @@ builder := compat.NewBuilder().
|
|||||||
builder := compat.NewBuilder().
|
builder := compat.NewBuilder().
|
||||||
WithOptions(
|
WithOptions(
|
||||||
"disable_file=true", // No files
|
"disable_file=true", // No files
|
||||||
"enable_stdout=true", // Console only
|
"enable_console=true", // Console only
|
||||||
"format=json", // For aggregators
|
"format=json", // For aggregators
|
||||||
"level=0", // Info and above
|
"level=0", // Info and above
|
||||||
)
|
)
|
||||||
|
|||||||
@ -28,7 +28,7 @@ All builder methods return `*ConfigBuilder` for chaining. Errors are accumulated
|
|||||||
| `Format(format string)` | `format`: Output format | Sets format ("txt", "json", "raw") |
|
| `Format(format string)` | `format`: Output format | Sets format ("txt", "json", "raw") |
|
||||||
| `BufferSize(size int64)` | `size`: Buffer size | Sets channel buffer size |
|
| `BufferSize(size int64)` | `size`: Buffer size | Sets channel buffer size |
|
||||||
| `MaxSizeKB(size int64)` | `size`: Size in MB | Sets max file size |
|
| `MaxSizeKB(size int64)` | `size`: Size in MB | Sets max file size |
|
||||||
| `EnableStdout(enable bool)` | `enable`: Boolean | Enables console output |
|
| `EnableConsole(enable bool)` | `enable`: Boolean | Enables console output |
|
||||||
| `DisableFile(disable bool)` | `disable`: Boolean | Disables file output |
|
| `DisableFile(disable bool)` | `disable`: Boolean | Disables file output |
|
||||||
| `HeartbeatLevel(level int64)` | `level`: 0-3 | Sets monitoring level |
|
| `HeartbeatLevel(level int64)` | `level`: 0-3 | Sets monitoring level |
|
||||||
|
|
||||||
|
|||||||
@ -60,11 +60,11 @@ logger.Info("info txt log record written to /var/log/myapp.txt")
|
|||||||
|-----------|------|-------------|---------|
|
|-----------|------|-------------|---------|
|
||||||
| `show_timestamp` | `bool` | Include timestamps in log entries | `true` |
|
| `show_timestamp` | `bool` | Include timestamps in log entries | `true` |
|
||||||
| `show_level` | `bool` | Include log level in entries | `true` |
|
| `show_level` | `bool` | Include log level in entries | `true` |
|
||||||
| `enable_stdout` | `bool` | Mirror logs to stdout/stderr | `false` |
|
| `enable_console` | `bool` | Mirror logs to stdout/stderr | `false` |
|
||||||
| `stdout_target` | `string` | Console target: `"stdout"`, `"stderr"`, or `"split"` | `"stdout"` |
|
| `console_target` | `string` | Console target: `"stdout"`, `"stderr"`, or `"split"` | `"stdout"` |
|
||||||
| `disable_file` | `bool` | Disable file output (console-only) | `false` |
|
| `disable_file` | `bool` | Disable file output (console-only) | `false` |
|
||||||
|
|
||||||
**Note:** When `stdout_target="split"`, INFO/DEBUG logs go to stdout while WARN/ERROR logs go to stderr.
|
**Note:** When `console_target="split"`, INFO/DEBUG logs go to stdout while WARN/ERROR logs go to stderr.
|
||||||
|
|
||||||
### Performance Tuning
|
### Performance Tuning
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ func TestFullLifecycle(t *testing.T) {
|
|||||||
Format("json").
|
Format("json").
|
||||||
MaxSizeKB(1).
|
MaxSizeKB(1).
|
||||||
BufferSize(1000).
|
BufferSize(1000).
|
||||||
EnableStdout(false).
|
EnableConsole(false).
|
||||||
HeartbeatLevel(1).
|
HeartbeatLevel(1).
|
||||||
HeartbeatIntervalS(2).
|
HeartbeatIntervalS(2).
|
||||||
Build()
|
Build()
|
||||||
@ -60,7 +60,7 @@ func TestFullLifecycle(t *testing.T) {
|
|||||||
logger.InfoTrace(2, "trace info")
|
logger.InfoTrace(2, "trace info")
|
||||||
|
|
||||||
// Apply runtime override
|
// Apply runtime override
|
||||||
err = logger.ApplyConfigString("enable_stdout=true", "stdout_target=stderr")
|
err = logger.ApplyConfigString("enable_console=true", "console_target=stderr")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// More logging after reconfiguration
|
// More logging after reconfiguration
|
||||||
|
|||||||
@ -430,9 +430,9 @@ func (l *Logger) applyConfig(cfg *Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup stdout writer based on config
|
// Setup stdout writer based on config
|
||||||
if cfg.EnableStdout {
|
if cfg.EnableConsole {
|
||||||
var writer io.Writer
|
var writer io.Writer
|
||||||
if cfg.StdoutTarget == "stderr" {
|
if cfg.ConsoleTarget == "stderr" {
|
||||||
writer = os.Stderr
|
writer = os.Stderr
|
||||||
} else {
|
} else {
|
||||||
writer = os.Stdout
|
writer = os.Stdout
|
||||||
|
|||||||
@ -89,12 +89,12 @@ func TestApplyConfigString(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "boolean values",
|
name: "boolean values",
|
||||||
configString: []string{
|
configString: []string{
|
||||||
"enable_stdout=true",
|
"enable_console=true",
|
||||||
"disable_file=false",
|
"disable_file=false",
|
||||||
"show_timestamp=false",
|
"show_timestamp=false",
|
||||||
},
|
},
|
||||||
verify: func(t *testing.T, cfg *Config) {
|
verify: func(t *testing.T, cfg *Config) {
|
||||||
assert.True(t, cfg.EnableStdout)
|
assert.True(t, cfg.EnableConsole)
|
||||||
assert.False(t, cfg.DisableFile)
|
assert.False(t, cfg.DisableFile)
|
||||||
assert.False(t, cfg.ShowTimestamp)
|
assert.False(t, cfg.ShowTimestamp)
|
||||||
},
|
},
|
||||||
@ -265,7 +265,7 @@ func TestLoggerStdoutMirroring(t *testing.T) {
|
|||||||
|
|
||||||
cfg := DefaultConfig()
|
cfg := DefaultConfig()
|
||||||
cfg.Directory = t.TempDir()
|
cfg.Directory = t.TempDir()
|
||||||
cfg.EnableStdout = true
|
cfg.EnableConsole = true
|
||||||
cfg.DisableFile = true
|
cfg.DisableFile = true
|
||||||
|
|
||||||
err := logger.ApplyConfig(cfg)
|
err := logger.ApplyConfig(cfg)
|
||||||
|
|||||||
@ -115,12 +115,12 @@ func (l *Logger) processLogRecord(record logRecord) int64 {
|
|||||||
dataLen := int64(len(data))
|
dataLen := int64(len(data))
|
||||||
|
|
||||||
// Mirror to stdout if enabled
|
// Mirror to stdout if enabled
|
||||||
enableStdout := c.EnableStdout
|
enableConsole := c.EnableConsole
|
||||||
if enableStdout {
|
if enableConsole {
|
||||||
if s := l.state.StdoutWriter.Load(); s != nil {
|
if s := l.state.StdoutWriter.Load(); s != nil {
|
||||||
if sinkWrapper, ok := s.(*sink); ok && sinkWrapper != nil {
|
if sinkWrapper, ok := s.(*sink); ok && sinkWrapper != nil {
|
||||||
// Handle split mode
|
// Handle split mode
|
||||||
if c.StdoutTarget == "split" {
|
if c.ConsoleTarget == "split" {
|
||||||
if record.Level >= LevelWarn {
|
if record.Level >= LevelWarn {
|
||||||
// Write WARN and ERROR to stderr
|
// Write WARN and ERROR to stderr
|
||||||
_, _ = os.Stderr.Write(data)
|
_, _ = os.Stderr.Write(data)
|
||||||
|
|||||||
Reference in New Issue
Block a user