1515package log
1616
1717import (
18+ "fmt"
1819 "io"
1920 llog "log"
21+ "log/slog"
2022 "os"
23+ "time"
2124
2225 "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql"
23- "go.uber.org/zap"
24- "go.uber.org/zap/zapcore"
26+ )
27+
28+ const (
29+ googLvlKey = "severity"
30+ googMsgKey = "message"
31+ googSourceKey = "sourceLocation"
32+ googTimeKey = "timestamp"
2533)
2634
2735// StdLogger is the standard logger that distinguishes between info and error
2836// logs.
2937type StdLogger struct {
30- infoLog * llog.Logger
31- debugLog * llog.Logger
32- errLog * llog.Logger
38+ stdLog * llog.Logger
39+ errLog * llog.Logger
3340}
3441
3542// NewStdLogger create a Logger that uses out and err for informational and
3643// error messages.
3744func NewStdLogger (out , err io.Writer ) cloudsql.Logger {
3845 return & StdLogger {
39- infoLog : llog .New (out , "" , llog .LstdFlags ),
40- debugLog : llog .New (out , "" , llog .LstdFlags ),
41- errLog : llog .New (err , "" , llog .LstdFlags ),
46+ stdLog : llog .New (out , "" , llog .LstdFlags ),
47+ errLog : llog .New (err , "" , llog .LstdFlags ),
4248 }
4349}
4450
4551// Infof logs informational messages
4652func (l * StdLogger ) Infof (format string , v ... interface {}) {
47- l .infoLog .Printf (format , v ... )
53+ l .stdLog .Printf (format , v ... )
4854}
4955
5056// Errorf logs error messages
@@ -54,61 +60,73 @@ func (l *StdLogger) Errorf(format string, v ...interface{}) {
5460
5561// Debugf logs debug messages
5662func (l * StdLogger ) Debugf (format string , v ... interface {}) {
57- l .debugLog .Printf (format , v ... )
63+ l .stdLog .Printf (format , v ... )
5864}
5965
6066// StructuredLogger writes log messages in JSON.
6167type StructuredLogger struct {
62- logger * zap.SugaredLogger
68+ stdLog * slog.Logger
69+ errLog * slog.Logger
6370}
6471
6572// Infof logs informational messages
6673func (l * StructuredLogger ) Infof (format string , v ... interface {}) {
67- l .logger . Infof ( format , v ... )
74+ l .stdLog . Info ( fmt . Sprintf ( format , v ... ) )
6875}
6976
7077// Errorf logs error messages
7178func (l * StructuredLogger ) Errorf (format string , v ... interface {}) {
72- l .logger . Errorf ( format , v ... )
79+ l .errLog . Error ( fmt . Sprintf ( format , v ... ) )
7380}
7481
7582// Debugf logs debug messages
7683func (l * StructuredLogger ) Debugf (format string , v ... interface {}) {
77- l .logger . Debugf ( format , v ... )
84+ l .stdLog . Debug ( fmt . Sprintf ( format , v ... ) )
7885}
7986
8087// NewStructuredLogger creates a Logger that logs messages using JSON.
81- func NewStructuredLogger (quiet bool ) (cloudsql.Logger , func () error ) {
82- // Configure structured logs to adhere to LogEntry format
83- // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
84- c := zap .NewProductionEncoderConfig ()
85- c .LevelKey = "severity"
86- c .MessageKey = "message"
87- c .TimeKey = "timestamp"
88- c .EncodeLevel = zapcore .CapitalLevelEncoder
89- c .EncodeTime = zapcore .ISO8601TimeEncoder
90-
91- enc := zapcore .NewJSONEncoder (c )
92-
93- var syncer zapcore.WriteSyncer
94- // quiet disables writing to the info log
88+ func NewStructuredLogger (quiet bool ) cloudsql.Logger {
89+ var infoHandler , errorHandler slog.Handler
9590 if quiet {
96- syncer = zapcore . AddSync ( io . Discard )
91+ infoHandler = slog . DiscardHandler
9792 } else {
98- syncer = zapcore .Lock (os .Stdout )
93+ infoHandler = slog .NewJSONHandler (os .Stdout , & slog.HandlerOptions {
94+ Level : slog .LevelDebug ,
95+ ReplaceAttr : replaceAttr ,
96+ })
9997 }
100- core := zapcore .NewTee (
101- zapcore .NewCore (enc , syncer , zap .LevelEnablerFunc (func (l zapcore.Level ) bool {
102- // Anything below error, goes to the info log.
103- return l < zapcore .ErrorLevel
104- })),
105- zapcore .NewCore (enc , zapcore .Lock (os .Stderr ), zap .LevelEnablerFunc (func (l zapcore.Level ) bool {
106- // Anything at error or higher goes to the error log.
107- return l >= zapcore .ErrorLevel
108- })),
109- )
98+ errorHandler = slog .NewJSONHandler (os .Stderr , & slog.HandlerOptions {
99+ Level : slog .LevelError ,
100+ ReplaceAttr : replaceAttr ,
101+ })
102+
110103 l := & StructuredLogger {
111- logger : zap .New (core ).Sugar (),
104+ stdLog : slog .New (infoHandler ),
105+ errLog : slog .New (errorHandler ),
106+ }
107+ return l
108+ }
109+
110+ // replaceAttr remaps default Go logging keys to adhere to LogEntry format
111+ // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
112+ func replaceAttr (groups []string , a slog.Attr ) slog.Attr {
113+ if groups == nil {
114+ if a .Key == slog .LevelKey {
115+ a .Key = googLvlKey
116+ return a
117+ } else if a .Key == slog .MessageKey {
118+ a .Key = googMsgKey
119+ return a
120+ } else if a .Key == slog .SourceKey {
121+ a .Key = googSourceKey
122+ return a
123+ } else if a .Key == slog .TimeKey {
124+ a .Key = googTimeKey
125+ if a .Value .Kind () == slog .KindTime {
126+ a .Value = slog .StringValue (a .Value .Time ().Format (time .RFC3339 ))
127+ }
128+ return a
129+ }
112130 }
113- return l , l . logger . Sync
131+ return a
114132}
0 commit comments