11package flagsmith
22
33import (
4- "log"
4+ "context"
5+ "fmt"
6+ "log/slog"
57 "os"
8+ "strings"
9+ "time"
10+
11+ "github.com/go-resty/resty/v2"
612)
713
814// Logger is the interface used for logging by flagsmith client. This interface defines the methods
@@ -19,33 +25,140 @@ type Logger interface {
1925 Debugf (format string , v ... interface {})
2026}
2127
22- func createLogger () * logger {
23- l := & logger {l : log .New (os .Stderr , "" , log .Ldate | log .Lmicroseconds )}
24- return l
28+ // slogToRestyAdapter adapts a slog.Logger to resty.Logger.
29+ type slogToRestyAdapter struct {
30+ logger * slog.Logger
31+ }
32+
33+ func newSlogToRestyAdapter (logger * slog.Logger ) * slogToRestyAdapter {
34+ return & slogToRestyAdapter {logger : logger }
35+ }
36+
37+ func (l * slogToRestyAdapter ) Errorf (format string , v ... interface {}) {
38+ l .logger .Error (format , v ... )
39+ }
40+
41+ func (l * slogToRestyAdapter ) Warnf (format string , v ... interface {}) {
42+ l .logger .Warn (format , v ... )
43+ }
44+
45+ func (l * slogToRestyAdapter ) Debugf (format string , v ... interface {}) {
46+ l .logger .Debug (format , v ... )
2547}
2648
27- var _ Logger = (* logger )(nil )
49+ // slogToLoggerAdapter adapts a slog.Logger to our Logger interface.
50+ type slogToLoggerAdapter struct {
51+ logger * slog.Logger
52+ }
53+
54+ func newSlogToLoggerAdapter (logger * slog.Logger ) * slogToLoggerAdapter {
55+ return & slogToLoggerAdapter {logger : logger }
56+ }
57+
58+ func (l * slogToLoggerAdapter ) Errorf (format string , v ... interface {}) {
59+ l .logger .Error (fmt .Sprintf (format , v ... ))
60+ }
2861
29- type logger struct {
30- l * log. Logger
62+ func ( l * slogToLoggerAdapter ) Warnf ( format string , v ... interface {}) {
63+ l . logger . Warn ( fmt . Sprintf ( format , v ... ))
3164}
3265
33- func (l * logger ) Errorf (format string , v ... interface {}) {
34- l .output ( "ERROR FLAGSMITH: " + format , v ... )
66+ func (l * slogToLoggerAdapter ) Debugf (format string , v ... interface {}) {
67+ l .logger . Debug ( fmt . Sprintf ( format , v ... ) )
3568}
3669
37- func (l * logger ) Warnf (format string , v ... interface {}) {
38- l .output ("WARN FLAGSMITH: " + format , v ... )
70+ // loggerToSlogAdapter adapts our Logger interface to a slog.Logger.
71+ type loggerToSlogAdapter struct {
72+ logger Logger
3973}
4074
41- func ( l * logger ) Debugf ( format string , v ... interface {}) {
42- l . output ( "DEBUG FLAGSMITH: " + format , v ... )
75+ func newLoggerToSlogAdapter ( logger Logger ) * slog. Logger {
76+ return slog . New ( & loggerToSlogAdapter { logger : logger } )
4377}
4478
45- func (l * logger ) output (format string , v ... interface {}) {
46- if len (v ) == 0 {
47- l .l .Print (format )
48- return
79+ // implement slog.Handler interface to adapt our Logger interface to a slog.Logger
80+
81+ func (l * loggerToSlogAdapter ) Enabled (ctx context.Context , level slog.Level ) bool {
82+ return true
83+ }
84+
85+ func (l * loggerToSlogAdapter ) Handle (ctx context.Context , r slog.Record ) error {
86+ msg := r .Message
87+ var attrs strings.Builder
88+ r .Attrs (func (a slog.Attr ) bool {
89+ attrs .WriteString (a .String () + " " )
90+ return true
91+ })
92+ msg += attrs .String ()
93+
94+ switch r .Level {
95+ case slog .LevelError :
96+ l .logger .Errorf (msg )
97+ case slog .LevelWarn :
98+ l .logger .Warnf (msg )
99+ case slog .LevelDebug :
100+ l .logger .Debugf (msg )
101+ }
102+ return nil
103+ }
104+
105+ func (l * loggerToSlogAdapter ) WithAttrs (_ []slog.Attr ) slog.Handler {
106+ return l
107+ }
108+
109+ func (l * loggerToSlogAdapter ) WithGroup (_ string ) slog.Handler {
110+ return l
111+ }
112+
113+ func createLogger () * slog.Logger {
114+ return slog .New (slog .NewTextHandler (os .Stderr , & slog.HandlerOptions {
115+ Level : slog .LevelDebug ,
116+ }))
117+ }
118+
119+ const (
120+ contextLoggerKey contextKey = contextKey ("logger" )
121+ contextStartTimeKey contextKey = contextKey ("startTime" )
122+ )
123+
124+ func newRestyLogRequestMiddleware (logger * slog.Logger ) resty.RequestMiddleware {
125+ return func (c * resty.Client , req * resty.Request ) error {
126+ // Create a child logger with request metadata
127+ reqLogger := logger .WithGroup ("http" ).With (
128+ "method" , req .Method ,
129+ "url" , req .URL ,
130+ )
131+ reqLogger .Debug ("request" )
132+
133+ // Store the logger in this request's context, and use it in the response
134+ req .SetContext (context .WithValue (req .Context (), contextLoggerKey , reqLogger ))
135+
136+ // Time the current request
137+ req .SetContext (context .WithValue (req .Context (), contextStartTimeKey , time .Now ()))
138+
139+ return nil
140+ }
141+ }
142+
143+ func newRestyLogResponseMiddleware (logger * slog.Logger ) resty.ResponseMiddleware {
144+ return func (client * resty.Client , resp * resty.Response ) error {
145+ // Retrieve the logger and start time from context
146+ reqLogger , _ := resp .Request .Context ().Value (contextLoggerKey ).(* slog.Logger )
147+ startTime , _ := resp .Request .Context ().Value (contextStartTimeKey ).(time.Time )
148+
149+ if reqLogger == nil {
150+ reqLogger = logger
151+ }
152+ reqLogger = reqLogger .With (
153+ slog .Int ("status" , resp .StatusCode ()),
154+ slog .Duration ("duration" , time .Since (startTime )),
155+ slog .Int64 ("content_length" , resp .Size ()),
156+ )
157+ if resp .IsError () {
158+ reqLogger .Error ("error response" )
159+ } else {
160+ reqLogger .Debug ("response" )
161+ }
162+ return nil
49163 }
50- l .l .Printf (format , v ... )
51164}
0 commit comments