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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (b *Builder) DisableFile(disable bool) *Builder {
|
||||
b.cfg.DisableFile = disable
|
||||
@ -118,6 +112,120 @@ func (b *Builder) HeartbeatIntervalS(interval int64) *Builder {
|
||||
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:
|
||||
// logger, err := log.NewBuilder().
|
||||
//
|
||||
@ -125,7 +233,7 @@ func (b *Builder) HeartbeatIntervalS(interval int64) *Builder {
|
||||
// LevelString("debug").
|
||||
// Format("json").
|
||||
// BufferSize(4096).
|
||||
// EnableStdout(true).
|
||||
// EnableConsole(true).
|
||||
// Build()
|
||||
//
|
||||
// if err == nil {
|
||||
|
||||
@ -20,7 +20,7 @@ func TestBuilder_Build(t *testing.T) {
|
||||
LevelString("debug").
|
||||
Format("json").
|
||||
BufferSize(2048).
|
||||
EnableStdout(true).
|
||||
EnableConsole(true).
|
||||
MaxSizeMB(10).
|
||||
HeartbeatLevel(2).
|
||||
Build()
|
||||
@ -43,7 +43,7 @@ func TestBuilder_Build(t *testing.T) {
|
||||
assert.Equal(t, LevelDebug, cfg.Level)
|
||||
assert.Equal(t, "json", cfg.Format)
|
||||
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(2), cfg.HeartbeatLevel)
|
||||
})
|
||||
|
||||
28
config.go
28
config.go
@ -46,9 +46,9 @@ type Config struct {
|
||||
HeartbeatIntervalS int64 `toml:"heartbeat_interval_s"` // Interval seconds for heartbeat
|
||||
|
||||
// Stdout/console output settings
|
||||
EnableStdout bool `toml:"enable_stdout"` // Mirror logs to stdout/stderr
|
||||
StdoutTarget string `toml:"stdout_target"` // "stdout" or "stderr"
|
||||
DisableFile bool `toml:"disable_file"` // Disable file output entirely
|
||||
EnableConsole bool `toml:"enable_console"` // Mirror logs to stdout/stderr
|
||||
ConsoleTarget string `toml:"console_target"` // "stdout" or "stderr"
|
||||
DisableFile bool `toml:"disable_file"` // Disable file output entirely
|
||||
|
||||
// Internal error handling
|
||||
InternalErrorsToStderr bool `toml:"internal_errors_to_stderr"` // Write internal errors to stderr
|
||||
@ -92,9 +92,9 @@ var defaultConfig = Config{
|
||||
HeartbeatIntervalS: 60,
|
||||
|
||||
// Stdout settings
|
||||
EnableStdout: false,
|
||||
StdoutTarget: "stdout",
|
||||
DisableFile: false,
|
||||
EnableConsole: false,
|
||||
ConsoleTarget: "stdout",
|
||||
DisableFile: false,
|
||||
|
||||
// Internal error handling
|
||||
InternalErrorsToStderr: false,
|
||||
@ -131,8 +131,8 @@ func (c *Config) Validate() error {
|
||||
return fmtErrorf("timestamp_format cannot be empty")
|
||||
}
|
||||
|
||||
if c.StdoutTarget != "stdout" && c.StdoutTarget != "stderr" && c.StdoutTarget != "split" {
|
||||
return fmtErrorf("invalid stdout_target: '%s' (use stdout, stderr, or split)", c.StdoutTarget)
|
||||
if c.ConsoleTarget != "stdout" && c.ConsoleTarget != "stderr" && c.ConsoleTarget != "split" {
|
||||
return fmtErrorf("invalid console_target: '%s' (use stdout, stderr, or split)", c.ConsoleTarget)
|
||||
}
|
||||
|
||||
// Numeric validations
|
||||
@ -315,15 +315,15 @@ func applyConfigField(cfg *Config, key, value string) error {
|
||||
}
|
||||
cfg.HeartbeatIntervalS = intVal
|
||||
|
||||
// Stdout/console output settings
|
||||
case "enable_stdout":
|
||||
// Console output settings
|
||||
case "enable_console":
|
||||
boolVal, err := strconv.ParseBool(value)
|
||||
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
|
||||
case "stdout_target":
|
||||
cfg.StdoutTarget = value
|
||||
cfg.EnableConsole = boolVal
|
||||
case "console_target":
|
||||
cfg.ConsoleTarget = value
|
||||
case "disable_file":
|
||||
boolVal, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
|
||||
@ -84,8 +84,8 @@ func TestConfigValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid stdout target",
|
||||
modify: func(c *Config) { c.StdoutTarget = "invalid" },
|
||||
wantError: "invalid stdout_target",
|
||||
modify: func(c *Config) { c.ConsoleTarget = "invalid" },
|
||||
wantError: "invalid console_target",
|
||||
},
|
||||
{
|
||||
name: "min > max check interval",
|
||||
|
||||
@ -286,7 +286,7 @@ builder := compat.NewBuilder().
|
||||
"format=txt", // Human-readable
|
||||
"level=-4", // Debug level
|
||||
"trace_depth=3", // Include traces
|
||||
"enable_stdout=true", // Console output
|
||||
"enable_console=true", // Console output
|
||||
"flush_interval_ms=50", // Quick feedback
|
||||
)
|
||||
```
|
||||
@ -297,7 +297,7 @@ builder := compat.NewBuilder().
|
||||
builder := compat.NewBuilder().
|
||||
WithOptions(
|
||||
"disable_file=true", // No files
|
||||
"enable_stdout=true", // Console only
|
||||
"enable_console=true", // Console only
|
||||
"format=json", // For aggregators
|
||||
"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") |
|
||||
| `BufferSize(size int64)` | `size`: Buffer size | Sets channel buffer 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 |
|
||||
| `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_level` | `bool` | Include log level in entries | `true` |
|
||||
| `enable_stdout` | `bool` | Mirror logs to stdout/stderr | `false` |
|
||||
| `stdout_target` | `string` | Console target: `"stdout"`, `"stderr"`, or `"split"` | `"stdout"` |
|
||||
| `enable_console` | `bool` | Mirror logs to stdout/stderr | `false` |
|
||||
| `console_target` | `string` | Console target: `"stdout"`, `"stderr"`, or `"split"` | `"stdout"` |
|
||||
| `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
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ func TestFullLifecycle(t *testing.T) {
|
||||
Format("json").
|
||||
MaxSizeKB(1).
|
||||
BufferSize(1000).
|
||||
EnableStdout(false).
|
||||
EnableConsole(false).
|
||||
HeartbeatLevel(1).
|
||||
HeartbeatIntervalS(2).
|
||||
Build()
|
||||
@ -60,7 +60,7 @@ func TestFullLifecycle(t *testing.T) {
|
||||
logger.InfoTrace(2, "trace info")
|
||||
|
||||
// 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)
|
||||
|
||||
// More logging after reconfiguration
|
||||
|
||||
@ -430,9 +430,9 @@ func (l *Logger) applyConfig(cfg *Config) error {
|
||||
}
|
||||
|
||||
// Setup stdout writer based on config
|
||||
if cfg.EnableStdout {
|
||||
if cfg.EnableConsole {
|
||||
var writer io.Writer
|
||||
if cfg.StdoutTarget == "stderr" {
|
||||
if cfg.ConsoleTarget == "stderr" {
|
||||
writer = os.Stderr
|
||||
} else {
|
||||
writer = os.Stdout
|
||||
|
||||
@ -89,12 +89,12 @@ func TestApplyConfigString(t *testing.T) {
|
||||
{
|
||||
name: "boolean values",
|
||||
configString: []string{
|
||||
"enable_stdout=true",
|
||||
"enable_console=true",
|
||||
"disable_file=false",
|
||||
"show_timestamp=false",
|
||||
},
|
||||
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.ShowTimestamp)
|
||||
},
|
||||
@ -265,7 +265,7 @@ func TestLoggerStdoutMirroring(t *testing.T) {
|
||||
|
||||
cfg := DefaultConfig()
|
||||
cfg.Directory = t.TempDir()
|
||||
cfg.EnableStdout = true
|
||||
cfg.EnableConsole = true
|
||||
cfg.DisableFile = true
|
||||
|
||||
err := logger.ApplyConfig(cfg)
|
||||
|
||||
@ -115,12 +115,12 @@ func (l *Logger) processLogRecord(record logRecord) int64 {
|
||||
dataLen := int64(len(data))
|
||||
|
||||
// Mirror to stdout if enabled
|
||||
enableStdout := c.EnableStdout
|
||||
if enableStdout {
|
||||
enableConsole := c.EnableConsole
|
||||
if enableConsole {
|
||||
if s := l.state.StdoutWriter.Load(); s != nil {
|
||||
if sinkWrapper, ok := s.(*sink); ok && sinkWrapper != nil {
|
||||
// Handle split mode
|
||||
if c.StdoutTarget == "split" {
|
||||
if c.ConsoleTarget == "split" {
|
||||
if record.Level >= LevelWarn {
|
||||
// Write WARN and ERROR to stderr
|
||||
_, _ = os.Stderr.Write(data)
|
||||
|
||||
Reference in New Issue
Block a user