diff --git a/cmd/picoclaw/internal/gateway/helpers.go b/cmd/picoclaw/internal/gateway/helpers.go index 91a5eeead..88f905edb 100644 --- a/cmd/picoclaw/internal/gateway/helpers.go +++ b/cmd/picoclaw/internal/gateway/helpers.go @@ -50,6 +50,10 @@ func gatewayCmd(debug bool) error { return fmt.Errorf("error loading config: %w", err) } + if cfg.Logger.TimeFormat != "" { + logger.SetTimeFormat(cfg.Logger.TimeFormat) + } + provider, modelID, err := providers.CreateProvider(cfg) if err != nil { return fmt.Errorf("error creating provider: %w", err) diff --git a/pkg/config/config.go b/pkg/config/config.go index 93e37d5fc..ed2508cd5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -81,6 +81,7 @@ type Config struct { Channels ChannelsConfig `json:"channels"` Providers ProvidersConfig `json:"providers,omitempty"` ModelList []ModelConfig `json:"model_list"` // New model-centric provider configuration + Logger LoggerConfig `json:"logger"` Gateway GatewayConfig `json:"gateway"` Tools ToolsConfig `json:"tools"` Heartbeat HeartbeatConfig `json:"heartbeat"` @@ -561,6 +562,10 @@ func (c *ModelConfig) Validate() error { return nil } +type LoggerConfig struct { + TimeFormat string `json:"time_format" env:"PICOCLAW_LOGGER_TIME_FORMAT"` +} + type GatewayConfig struct { Host string `json:"host" env:"PICOCLAW_GATEWAY_HOST"` Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"` diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 4424b0eb1..df6831799 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -342,6 +342,9 @@ func DefaultConfig() *Config { APIKey: "", }, }, + Logger: LoggerConfig{ + TimeFormat: "15:04:05", + }, Gateway: GatewayConfig{ Host: "127.0.0.1", Port: 18790, diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index db13eaea9..1f2e82baf 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -30,9 +30,10 @@ var ( FATAL: "FATAL", } - currentLevel = INFO - logger zerolog.Logger - fileLogger zerolog.Logger + currentLevel = INFO + currentTimeFormat = "15:04:05" + logger zerolog.Logger + fileLogger zerolog.Logger logFile *os.File once sync.Once mu sync.RWMutex @@ -44,7 +45,7 @@ func init() { consoleWriter := zerolog.ConsoleWriter{ Out: os.Stdout, - TimeFormat: "15:04:05", // TODO: make it configurable??? + TimeFormat: currentTimeFormat, } logger = zerolog.New(consoleWriter).With().Timestamp().Logger() @@ -52,6 +53,19 @@ func init() { }) } +func SetTimeFormat(format string) { + mu.Lock() + defer mu.Unlock() + currentTimeFormat = format + + consoleWriter := zerolog.ConsoleWriter{ + Out: os.Stdout, + TimeFormat: currentTimeFormat, + } + + logger = zerolog.New(consoleWriter).With().Timestamp().Logger() +} + func SetLevel(level LogLevel) { mu.Lock() defer mu.Unlock() diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 6e6f8dfa8..c8b31ec8a 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -137,3 +137,15 @@ func TestLoggerHelperFunctions(t *testing.T) { DebugC("test", "Debug with component") WarnF("Warning with fields", map[string]any{"key": "value"}) } + +func TestSetTimeFormat(t *testing.T) { + // This test just ensures that calling SetTimeFormat doesn't panic + // and updates the internal state. Since the logger is global and + // writes to stdout, we don't easily capture the output here without + // more complex mocking. + SetTimeFormat("2006-01-02 15:04:05") + Info("Testing time format change") + + // Restore default + SetTimeFormat("15:04:05") +}