Skip to content

Commit d0643cd

Browse files
committed
chore: logging improvements
1 parent 1c75c8d commit d0643cd

7 files changed

Lines changed: 90 additions & 43 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
bin
2+
y
13
# Binaries for programs and plugins
24
*.exe
35
*.exe~

http/client.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,15 +459,16 @@ func (c *Client) TraceToStdout(config TraceConfig) *Client {
459459
}
460460

461461
// WithHttpLogging enables HTTP request/response logging based on the provided log levels.
462-
//
462+
//
463463
// Parameters:
464464
// - headerLevel: The minimum log level required to log HTTP headers (e.g., logger.Debug)
465465
// - bodyLevel: The minimum log level required to log request/response bodies (e.g., logger.Trace)
466466
//
467467
// Example:
468-
// client.WithHttpLogging(logger.Debug, logger.Trace)
469-
//
470-
// This will log headers when debug logging is enabled (-v or -v 1) and
468+
//
469+
// client.WithHttpLogging(logger.Debug, logger.Trace)
470+
//
471+
// This will log headers when debug logging is enabled (-v or -v 1) and
471472
// bodies when trace logging is enabled (-vv or -v 2 or higher).
472473
//
473474
// Note: When using with cobra commands, ensure UseCobraFlags is called

http/response.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,29 @@ func (h *Response) HeaderMap() map[string]string {
101101
return headers
102102
}
103103

104+
func (h *Response) AsMap() (map[string]any, error) {
105+
106+
m := make(map[string]any)
107+
108+
m["headers"] = map[string]any{}
109+
for k, v := range logger.StripSecretsFromMap(h.HeaderMap()) {
110+
m["headers"].(map[string]any)[k] = v
111+
}
112+
113+
if h.Request != nil && h.Request.url != nil {
114+
m["url"] = h.Request.url.String()
115+
}
116+
m["status"] = h.StatusCode
117+
118+
if h.IsJSON() {
119+
m["body"], _ = h.AsJSON()
120+
} else {
121+
m["body"], _ = h.AsString()
122+
}
123+
124+
return m, nil
125+
}
126+
104127
func (h *Response) Debug() string {
105128
// mimic the response, + add content-type and size
106129
var sb strings.Builder

logger/buffered.go

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -288,42 +288,7 @@ func (b *BufferedLogger) GetLevel() LogLevel {
288288
func (b *BufferedLogger) SetLogLevel(level any) {
289289
b.mu.Lock()
290290
defer b.mu.Unlock()
291-
292-
oldLevel := b.logLevel
293-
294-
switch v := level.(type) {
295-
case LogLevel:
296-
b.logLevel = v
297-
case int:
298-
b.logLevel = LogLevel(v)
299-
case string:
300-
// Parse string level
301-
switch v {
302-
case "trace":
303-
b.logLevel = Trace
304-
case "debug":
305-
b.logLevel = Debug
306-
case "info":
307-
b.logLevel = Info
308-
case "warn":
309-
b.logLevel = Warn
310-
case "error":
311-
b.logLevel = Error
312-
case "fatal":
313-
b.logLevel = Fatal
314-
default:
315-
b.logLevel = Info
316-
}
317-
default:
318-
b.logLevel = Info
319-
}
320-
321-
// Auto-scale retention if log level changed
322-
if oldLevel != b.logLevel {
323-
b.mu.Unlock() // Unlock temporarily for ScaleRetentionByLogLevel
324-
b.ScaleRetentionByLogLevel()
325-
b.mu.Lock() // Re-lock for defer
326-
}
291+
b.logLevel = ParseLevel(b, level)
327292
}
328293

329294
// SetMinLogLevel sets the minimum log level (same as SetLogLevel for BufferedLogger)

logger/default.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package logger
22

33
import (
44
"fmt"
5+
"io"
56
"os"
67
"regexp"
78
"strings"
@@ -231,6 +232,17 @@ func SetLogger(logger Logger) {
231232
currentLogger = logger
232233
}
233234

235+
// Use configures the logger to write to the specified writer.
236+
// This replaces the current logger with one that outputs to the given writer.
237+
// Useful for integrating with test frameworks like Ginkgo.
238+
//
239+
// Example:
240+
//
241+
// logger.Use(GinkgoWriter) // Route logger output to Ginkgo's test writer
242+
func Use(writer io.Writer) {
243+
currentLogger = NewWithWriter(writer)
244+
}
245+
234246
// StandardLogger returns the current global logger instance.
235247
// This is equivalent to GetLogger().
236248
func StandardLogger() Logger {

logger/http.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var SensitiveHeaders = []string{
1616

1717
// NewHttpLogger creates an HTTP logger that logs at predefined levels.
1818
// Deprecated: Use NewHttpLoggerWithLevels for more control over logging levels.
19-
//
19+
//
2020
// Default behavior:
2121
// - Headers and timing: Requires log level 5 (Trace3)
2222
// - Request body: Requires log level 6 (Trace4)
@@ -51,8 +51,9 @@ func NewHttpLogger(logger Logger, rt http.RoundTripper) http.RoundTripper {
5151
// - bodyLevel: Minimum log level required to log request/response bodies
5252
//
5353
// Example:
54-
// // Log headers at debug level (-v) and bodies at trace level (-vv)
55-
// transport := NewHttpLoggerWithLevels(logger, http.DefaultTransport, logger.Debug, logger.Trace)
54+
//
55+
// // Log headers at debug level (-v) and bodies at trace level (-vv)
56+
// transport := NewHttpLoggerWithLevels(logger, http.DefaultTransport, logger.Debug, logger.Trace)
5657
func NewHttpLoggerWithLevels(logger Logger, rt http.RoundTripper, headerLevel, bodyLevel LogLevel) http.RoundTripper {
5758
if !logger.IsLevelEnabled(headerLevel) {
5859
return rt

logger/slog.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package logger
33
import (
44
"context"
55
"fmt"
6+
"io"
67
"log/slog"
78
"net/http"
89
"os"
@@ -135,6 +136,48 @@ func New(prefix string) *SlogLogger {
135136
// logger.V(4).Infof("new logger created name=%v flags=%s level=%s", prefix, flags.level, FromSlogLevel(lvl.Level()).String())
136137
return logger
137138
}
139+
140+
// NewWithWriter creates a new SlogLogger that writes to the specified writer.
141+
// This is useful for integrating with test frameworks or custom output destinations.
142+
func NewWithWriter(writer io.Writer) *SlogLogger {
143+
var logger *SlogLogger
144+
var lvl = &slog.LevelVar{}
145+
146+
reportCaller := properties.On(flags.reportCaller, "log.caller")
147+
logJson := properties.On(flags.jsonLogs, "log.json")
148+
logColor := properties.On(flags.color, "log.color")
149+
150+
var rootLevel string
151+
if flags.level != "" {
152+
rootLevel = flags.level
153+
} else {
154+
rootLevel = properties.String("info", "log.level")
155+
}
156+
157+
if logJson {
158+
logger = &SlogLogger{
159+
Level: lvl,
160+
Logger: slog.New(slog.NewJSONHandler(writer, &slog.HandlerOptions{
161+
AddSource: reportCaller,
162+
Level: lvl,
163+
})),
164+
}
165+
} else {
166+
logger = &SlogLogger{
167+
Logger: slog.New(tint.NewHandler(writer, &tint.Options{
168+
Level: lvl,
169+
NoColor: !logColor,
170+
AddSource: reportCaller,
171+
TimeFormat: properties.String("15:04:05.999", "log.time.format"),
172+
})),
173+
Level: lvl,
174+
}
175+
}
176+
177+
logger.SetLogLevel(rootLevel)
178+
return logger
179+
}
180+
138181
func UseSlog() {
139182
if currentLogger != nil {
140183
return

0 commit comments

Comments
 (0)