v0.1.11 harden logging lifecycle, sanitization, and formatter edge cases
Test / test (push) Canceled after 0s
Test / fuzz (./compat, FuzzParseFormat) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatJSON) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatTextPolicy) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSanitize) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSerializerJSON) (push) Canceled after 0s
Test / test (push) Canceled after 0s
Test / fuzz (./compat, FuzzParseFormat) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatJSON) (push) Canceled after 0s
Test / fuzz (./formatter, FuzzFormatTextPolicy) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSanitize) (push) Canceled after 0s
Test / fuzz (./sanitizer, FuzzSerializerJSON) (push) Canceled after 0s
This commit is contained in:
1 parent
79a81db3ca
commit
2417e51da1
46 files changed
+1846
-1289
No files matched your search
@@ -2,6 +2,7 @@ package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -117,11 +118,24 @@ func (c *Config) Clone() *Config {
|
||||
|
||||
// Validate performs validation on the configuration
|
||||
func (c *Config) Validate() error {
|
||||
if c == nil {
|
||||
return fmtErrorf("configuration cannot be nil")
|
||||
}
|
||||
// String validations
|
||||
if strings.TrimSpace(c.Name) == "" {
|
||||
return fmtErrorf("log name cannot be empty")
|
||||
}
|
||||
|
||||
if c.Name == "." || c.Name == ".." || strings.ContainsAny(c.Name, "/\\\x00") || strings.ContainsAny(c.Extension, "/\\\x00") {
|
||||
return fmtErrorf("name and extension must be filename components, without path separators or NUL")
|
||||
}
|
||||
if c.EnableFile && strings.TrimSpace(c.Directory) == "" {
|
||||
return fmtErrorf("directory cannot be empty when file output is enabled")
|
||||
}
|
||||
if c.Level == levelOff {
|
||||
return fmtErrorf("level is reserved for disabling emission")
|
||||
}
|
||||
|
||||
if c.Format != "txt" && c.Format != "json" && c.Format != "raw" {
|
||||
return fmtErrorf("invalid format: '%s' (use txt, json, or raw)", c.Format)
|
||||
}
|
||||
@@ -150,6 +164,14 @@ func (c *Config) Validate() error {
|
||||
return fmtErrorf("buffer_size must be positive: %d", c.BufferSize)
|
||||
}
|
||||
|
||||
// Bound allocation before make(chan): one million records already reserves
|
||||
// over 100 MiB and is well beyond the default queue capacity.
|
||||
if c.BufferSize > 1<<20 {
|
||||
return fmtErrorf("buffer_size cannot exceed 1048576 records")
|
||||
}
|
||||
if c.MaxSizeKB > math.MaxInt64/sizeMultiplier || c.MaxTotalSizeKB > math.MaxInt64/sizeMultiplier || c.MinDiskFreeKB > math.MaxInt64/sizeMultiplier {
|
||||
return fmtErrorf("size limits overflow byte counts")
|
||||
}
|
||||
if c.MaxSizeKB < 0 || c.MaxTotalSizeKB < 0 || c.MinDiskFreeKB < 0 {
|
||||
return fmtErrorf("size limits cannot be negative")
|
||||
}
|
||||
@@ -159,6 +181,27 @@ func (c *Config) Validate() error {
|
||||
return fmtErrorf("interval settings must be positive")
|
||||
}
|
||||
|
||||
for _, ms := range []int64{c.FlushIntervalMs, c.DiskCheckIntervalMs, c.MinCheckIntervalMs, c.MaxCheckIntervalMs} {
|
||||
if ms > math.MaxInt64/int64(time.Millisecond) {
|
||||
return fmtErrorf("interval overflows time.Duration")
|
||||
}
|
||||
}
|
||||
if c.FlushIntervalMs > math.MaxInt64/(2*int64(time.Millisecond)) {
|
||||
return fmtErrorf("flush interval overflows default stop timeout")
|
||||
}
|
||||
if c.HeartbeatIntervalS > math.MaxInt64/int64(time.Second) {
|
||||
return fmtErrorf("heartbeat interval overflows time.Duration")
|
||||
}
|
||||
for _, setting := range []struct{ value, unit float64 }{
|
||||
{c.RetentionPeriodHrs, float64(time.Hour)},
|
||||
{c.RetentionCheckMins, float64(time.Minute)},
|
||||
} {
|
||||
ns := setting.value * setting.unit
|
||||
if math.IsNaN(ns) || math.IsInf(ns, 0) || ns >= float64(math.MaxInt64) || setting.value > 0 && ns < 1 {
|
||||
return fmtErrorf("retention settings must be finite and representable as time.Duration")
|
||||
}
|
||||
}
|
||||
|
||||
if c.TraceDepth < 0 || c.TraceDepth > 10 {
|
||||
return fmtErrorf("trace_depth must be between 0 and 10: %d", c.TraceDepth)
|
||||
}
|
||||
@@ -381,6 +424,8 @@ func configRequiresRestart(oldCfg, newCfg *Config) bool {
|
||||
if oldCfg.FlushIntervalMs != newCfg.FlushIntervalMs ||
|
||||
oldCfg.DiskCheckIntervalMs != newCfg.DiskCheckIntervalMs ||
|
||||
oldCfg.EnableAdaptiveInterval != newCfg.EnableAdaptiveInterval ||
|
||||
oldCfg.MinCheckIntervalMs != newCfg.MinCheckIntervalMs ||
|
||||
oldCfg.MaxCheckIntervalMs != newCfg.MaxCheckIntervalMs ||
|
||||
oldCfg.HeartbeatIntervalS != newCfg.HeartbeatIntervalS ||
|
||||
oldCfg.HeartbeatLevel != newCfg.HeartbeatLevel ||
|
||||
oldCfg.RetentionCheckMins != newCfg.RetentionCheckMins ||
|
||||
@@ -411,4 +456,4 @@ func combineConfigErrors(errors []error) error {
|
||||
sb.WriteString(fmt.Sprintf("\n %d. %s", i+1, errMsg))
|
||||
}
|
||||
return fmt.Errorf("%s", sb.String())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user