diff --git a/internal/logging/global_logger.go b/internal/logging/global_logger.go index f78477d4..3b034dc6 100644 --- a/internal/logging/global_logger.go +++ b/internal/logging/global_logger.go @@ -6,7 +6,6 @@ import ( "io" "os" "path/filepath" - "sort" "strings" "sync" @@ -30,6 +29,9 @@ var ( // Format: [2025-12-23 20:14:04] [debug] [manager.go:524] | a1b2c3d4 | Use API key sk-9...0RHO for model gpt-5.2 type LogFormatter struct{} +// logFieldOrder defines the display order for common log fields. +var logFieldOrder = []string{"provider", "model", "mode", "budget", "level", "original_value", "min", "max", "clamped_to", "error"} + // Format renders a single log entry with custom formatting. func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) { var buffer *bytes.Buffer @@ -56,17 +58,19 @@ func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) { // Build fields string (excluding request_id which is already shown) var fieldsStr string if len(entry.Data) > 0 { - var keys []string - for k := range entry.Data { - if k == "request_id" { + seen := make(map[string]bool) + var fields []string + for _, k := range logFieldOrder { + if v, ok := entry.Data[k]; ok { + fields = append(fields, fmt.Sprintf("%s=%v", k, v)) + seen[k] = true + } + } + for k, v := range entry.Data { + if k == "request_id" || seen[k] { continue } - keys = append(keys, k) - } - sort.Strings(keys) - var fields []string - for _, k := range keys { - fields = append(fields, fmt.Sprintf("%s=%v", k, entry.Data[k])) + fields = append(fields, fmt.Sprintf("%s=%v", k, v)) } if len(fields) > 0 { fieldsStr = " " + strings.Join(fields, " ") diff --git a/internal/thinking/apply.go b/internal/thinking/apply.go index 45898bd0..0b26ca0b 100644 --- a/internal/thinking/apply.go +++ b/internal/thinking/apply.go @@ -149,7 +149,7 @@ func ApplyThinking(body []byte, model string, provider string) ([]byte, error) { "provider": provider, "model": modelInfo.ID, "error": err.Error(), - }).Warn("thinking: validation failed, returning original body") + }).Warn("thinking: validation failed") // Return original body on validation failure (defensive programming). // This ensures callers who ignore the error won't receive nil body. // The upstream service will decide how to handle the unmodified request. diff --git a/internal/thinking/validate.go b/internal/thinking/validate.go index 66f8160c..886f3161 100644 --- a/internal/thinking/validate.go +++ b/internal/thinking/validate.go @@ -76,12 +76,10 @@ func ClampBudgetWithZeroCheck(value, min, max int, zeroAllowed bool) int { return 0 } log.WithFields(log.Fields{ - "original_value": value, - "clamped_to": min, - "min": min, - "max": max, - "reason": "zero_not_allowed", - }).Warn("budget clamped: zero not allowed") + "clamped_to": min, + "min": min, + "max": max, + }).Warn("thinking: budget zero not allowed") return min } @@ -253,8 +251,8 @@ func convertAutoToMidRange(config ThinkingConfig, support *registry.ThinkingSupp func logClamp(original, clampedTo, min, max int) { log.WithFields(log.Fields{ "original_value": original, - "clamped_to": clampedTo, "min": min, "max": max, - }).Debug("budget clamped: value outside model range") + "clamped_to": clampedTo, + }).Debug("thinking: budget clamped") }