e3.1.2 Changed output stdout to console for clarity.

This commit is contained in:
2025-09-29 04:47:48 -04:00
parent 2234123f59
commit d58b61067f
11 changed files with 149 additions and 41 deletions

View File

@ -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 {

View File

@ -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)
}) })

View File

@ -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 {

View File

@ -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",

View File

@ -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
) )

View File

@ -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 |

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)