Skip to content

Commit 82e4d47

Browse files
committed
Replace slog with zap for global logging
* Switch from slog to uber-go/zap SugaredLogger for better performance * Add *w functions (Debugw, Infow, etc.) for structured key-value logging * Add DPanic and Fatal log levels with corresponding *f/*w variants * Maintain structured/unstructured log switching via UNSTRUCTURED_LOGS * Preserve debug flag integration for log level control * Use go-logr/zapr for NewLogr() Kubernetes logr.Logger compatibility
1 parent 74b6fa7 commit 82e4d47

File tree

6 files changed

+290
-301
lines changed

6 files changed

+290
-301
lines changed

cmd/thv-operator/controllers/mcpserver_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (r *MCPServerReconciler) createRBACResource(
284284
) error {
285285
desired := createResource()
286286
if err := controllerutil.SetControllerReference(mcpServer, desired, r.Scheme); err != nil {
287-
logger.Error(fmt.Sprintf("Failed to set controller reference for %s", resourceType), err)
287+
logger.Errorf("Failed to set controller reference for %s: %v", resourceType, err)
288288
return nil
289289
}
290290

@@ -310,7 +310,7 @@ func (r *MCPServerReconciler) updateRBACResourceIfNeeded(
310310
) error {
311311
desired := createResource()
312312
if err := controllerutil.SetControllerReference(mcpServer, desired, r.Scheme); err != nil {
313-
logger.Error(fmt.Sprintf("Failed to set controller reference for %s", resourceType), err)
313+
logger.Errorf("Failed to set controller reference for %s: %v", resourceType, err)
314314
return nil
315315
}
316316

@@ -665,7 +665,7 @@ func (r *MCPServerReconciler) deploymentForMCPServer(m *mcpv1alpha1.MCPServer) *
665665

666666
// Set MCPServer instance as the owner and controller
667667
if err := controllerutil.SetControllerReference(m, dep, r.Scheme); err != nil {
668-
logger.Error("Failed to set controller reference for Deployment", err)
668+
logger.Errorf("Failed to set controller reference for Deployment %v", err)
669669
return nil
670670
}
671671
return dep
@@ -752,7 +752,7 @@ func (r *MCPServerReconciler) serviceForMCPServer(m *mcpv1alpha1.MCPServer) *cor
752752

753753
// Set MCPServer instance as the owner and controller
754754
if err := controllerutil.SetControllerReference(m, svc, r.Scheme); err != nil {
755-
logger.Error("Failed to set controller reference for Service", err)
755+
logger.Errorf("Failed to set controller reference for Service %v", err)
756756
return nil
757757
}
758758
return svc

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ require (
1212
github.com/docker/docker v28.3.3+incompatible
1313
github.com/docker/go-connections v0.6.0
1414
github.com/go-chi/chi/v5 v5.2.2
15+
github.com/go-logr/zapr v1.3.0
1516
github.com/gofrs/flock v0.12.1
1617
github.com/google/go-containerregistry v0.20.6
1718
github.com/google/uuid v1.6.0
1819
github.com/lestrrat-go/httprc/v3 v3.0.1
1920
github.com/lestrrat-go/jwx/v3 v3.0.10
20-
github.com/lmittmann/tint v1.1.2
2121
github.com/mark3labs/mcp-go v0.37.0
2222
github.com/olekukonko/tablewriter v1.0.9
2323
github.com/onsi/ginkgo/v2 v2.24.0
@@ -26,7 +26,6 @@ require (
2626
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
2727
github.com/prometheus/client_golang v1.23.0
2828
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
29-
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
3029
github.com/sigstore/protobuf-specs v0.5.0
3130
github.com/sigstore/sigstore-go v1.1.1
3231
github.com/spf13/viper v1.20.1
@@ -41,6 +40,7 @@ require (
4140
go.opentelemetry.io/otel/sdk v1.37.0
4241
go.opentelemetry.io/otel/sdk/metric v1.37.0
4342
go.uber.org/mock v0.6.0
43+
go.uber.org/zap v1.27.0
4444
golang.ngrok.com/ngrok/v2 v2.0.0
4545
golang.org/x/exp/jsonrpc2 v0.0.0-20250813145105-42675adae3e6
4646
golang.org/x/mod v0.27.0
@@ -242,7 +242,6 @@ require (
242242
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
243243
go.uber.org/automaxprocs v1.6.0 // indirect
244244
go.uber.org/multierr v1.11.0 // indirect
245-
go.uber.org/zap v1.27.0 // indirect
246245
go.yaml.in/yaml/v2 v2.4.2 // indirect
247246
golang.ngrok.com/muxado/v2 v2.0.1 // indirect
248247
golang.org/x/exp/event v0.0.0-20250718183923-645b1fa84792 // indirect

go.sum

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,8 +1288,6 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
12881288
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
12891289
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
12901290
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
1291-
github.com/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w=
1292-
github.com/lmittmann/tint v1.1.2/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
12931291
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
12941292
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
12951293
github.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQLANWwZt2ULeIC8yMNYs=
@@ -1465,7 +1463,6 @@ github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsF
14651463
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
14661464
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
14671465
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
1468-
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
14691466
github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A=
14701467
github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=
14711468
github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4=

pkg/logger/logger.go

Lines changed: 82 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -2,193 +2,159 @@
22
package logger
33

44
import (
5-
"context"
6-
"fmt"
7-
"log/slog"
85
"os"
9-
"runtime"
106
"strconv"
117
"time"
128

13-
"github.com/lmittmann/tint"
9+
"github.com/go-logr/logr"
10+
"github.com/go-logr/zapr"
1411
"github.com/spf13/viper"
12+
"go.uber.org/zap"
13+
"go.uber.org/zap/zapcore"
1514
)
1615

17-
// Log is a global logger instance
18-
var log Logger
19-
2016
// Debug logs a message at debug level using the singleton logger.
21-
func Debug(msg string, args ...any) {
22-
log.Debug(msg, args...)
17+
func Debug(msg string) {
18+
zap.S().Debug(msg)
2319
}
2420

2521
// Debugf logs a message at debug level using the singleton logger.
2622
func Debugf(msg string, args ...any) {
27-
log.Debugf(msg, args...)
23+
zap.S().Debugf(msg, args...)
24+
}
25+
26+
// Debugw logs a message at debug level using the singleton logger with additional key-value pairs.
27+
func Debugw(msg string, keysAndValues ...any) {
28+
zap.S().Debugw(msg, keysAndValues...)
2829
}
2930

3031
// Info logs a message at info level using the singleton logger.
31-
func Info(msg string, args ...any) {
32-
log.Info(msg, args...)
32+
func Info(msg string) {
33+
zap.S().Info(msg)
3334
}
3435

3536
// Infof logs a message at info level using the singleton logger.
3637
func Infof(msg string, args ...any) {
37-
log.Infof(msg, args...)
38+
zap.S().Infof(msg, args...)
39+
}
40+
41+
// Infow logs a message at info level using the singleton logger with additional key-value pairs.
42+
func Infow(msg string, keysAndValues ...any) {
43+
zap.S().Infow(msg, keysAndValues...)
3844
}
3945

4046
// Warn logs a message at warning level using the singleton logger.
41-
func Warn(msg string, args ...any) {
42-
log.Warn(msg, args...)
47+
func Warn(msg string) {
48+
zap.S().Warn(msg)
4349
}
4450

4551
// Warnf logs a message at warning level using the singleton logger.
4652
func Warnf(msg string, args ...any) {
47-
log.Warnf(msg, args...)
53+
zap.S().Warnf(msg, args...)
54+
}
55+
56+
// Warnw logs a message at warning level using the singleton logger with additional key-value pairs.
57+
func Warnw(msg string, keysAndValues ...any) {
58+
zap.S().Warnw(msg, keysAndValues...)
4859
}
4960

5061
// Error logs a message at error level using the singleton logger.
51-
func Error(msg string, args ...any) {
52-
log.Error(msg, args...)
62+
func Error(msg string) {
63+
zap.S().Error(msg)
5364
}
5465

5566
// Errorf logs a message at error level using the singleton logger.
5667
func Errorf(msg string, args ...any) {
57-
log.Errorf(msg, args...)
68+
zap.S().Errorf(msg, args...)
69+
}
70+
71+
// Errorw logs a message at error level using the singleton logger with additional key-value pairs.
72+
func Errorw(msg string, keysAndValues ...any) {
73+
zap.S().Errorw(msg, keysAndValues...)
5874
}
5975

6076
// Panic logs a message at error level using the singleton logger and panics the program.
6177
func Panic(msg string) {
62-
log.Panic(msg)
78+
zap.S().Panic(msg)
6379
}
6480

6581
// Panicf logs a message at error level using the singleton logger and panics the program.
6682
func Panicf(msg string, args ...any) {
67-
log.Panicf(msg, args...)
83+
zap.S().Panicf(msg, args...)
6884
}
6985

70-
// Logger provides a unified interface for logging
71-
type Logger interface {
72-
Debug(msg string, args ...any)
73-
Debugf(msg string, args ...any)
74-
Info(msg string, args ...any)
75-
Infof(msg string, args ...any)
76-
Warn(msg string, args ...any)
77-
Warnf(msg string, args ...any)
78-
Error(msg string, args ...any)
79-
Errorf(msg string, args ...any)
80-
Panic(msg string)
81-
Panicf(msg string, args ...any)
86+
// Panicw logs a message at error level using the singleton logger with additional key-value pairs and panics the program.
87+
func Panicw(msg string, keysAndValues ...any) {
88+
zap.S().Panicw(msg, keysAndValues...)
8289
}
8390

84-
// Implementation using slog
85-
type slogLogger struct {
86-
logger *slog.Logger
91+
// DPanic logs a message at error level using the singleton logger and panics the program.
92+
func DPanic(msg string) {
93+
zap.S().DPanic(msg)
8794
}
8895

89-
func (l *slogLogger) Debugf(msg string, args ...any) {
90-
l.logger.Debug(fmt.Sprintf(msg, args...))
96+
// DPanicf logs a message at error level using the singleton logger and panics the program.
97+
func DPanicf(msg string, args ...any) {
98+
zap.S().DPanicf(msg, args...)
9199
}
92100

93-
func (l *slogLogger) Infof(msg string, args ...any) {
94-
l.logger.Info(fmt.Sprintf(msg, args...))
101+
// DPanicw logs a message at error level using the singleton logger with additional key-value pairs and panics the program.
102+
func DPanicw(msg string, keysAndValues ...any) {
103+
zap.S().DPanicw(msg, keysAndValues...)
95104
}
96105

97-
func (l *slogLogger) Warnf(msg string, args ...any) {
98-
l.logger.Warn(fmt.Sprintf(msg, args...))
106+
// Fatal logs a message at error level using the singleton logger and exits the program.
107+
func Fatal(msg string) {
108+
zap.S().Fatal(msg)
99109
}
100110

101-
func (l *slogLogger) Errorf(msg string, args ...any) {
102-
l.logger.Error(fmt.Sprintf(msg, args...))
111+
// Fatalf logs a message at error level using the singleton logger and exits the program.
112+
func Fatalf(msg string, args ...any) {
113+
zap.S().Fatalf(msg, args...)
103114
}
104115

105-
func (l *slogLogger) Panicf(msg string, args ...any) {
106-
l.Panic(fmt.Sprintf(msg, args...))
116+
// Fatalw logs a message at error level using the singleton logger with additional key-value pairs and exits the program.
117+
func Fatalw(msg string, keysAndValues ...any) {
118+
zap.S().Fatalw(msg, keysAndValues...)
107119
}
108120

109-
func (l *slogLogger) Debug(msg string, args ...any) {
110-
l.logger.Debug(msg, args...)
111-
}
112-
113-
func (l *slogLogger) Info(msg string, args ...any) {
114-
l.logger.Info(msg, args...)
115-
}
116-
117-
func (l *slogLogger) Warn(msg string, args ...any) {
118-
l.logger.Warn(msg, args...)
119-
}
120-
121-
func (l *slogLogger) Error(msg string, args ...any) {
122-
l.logger.Error(msg, args...)
123-
}
124-
125-
func (l *slogLogger) Panic(msg string) {
126-
var pcs [1]uintptr
127-
runtime.Callers(2, pcs[:]) // skip [Callers, Panic]
128-
record := slog.NewRecord(time.Now(), slog.LevelError, msg, pcs[0])
129-
_ = l.logger.Handler().Handle(context.Background(), record)
130-
panic(msg)
131-
}
132-
133-
func unstructuredLogs() bool {
134-
unstructuredLogs, err := strconv.ParseBool(os.Getenv("UNSTRUCTURED_LOGS"))
135-
if err != nil {
136-
// at this point if the error is not nil, the env var wasn't set, or is ""
137-
// which means we just default to outputting unstructured logs.
138-
return true
139-
}
140-
return unstructuredLogs
121+
// NewLogr returns a logr.Logger which uses zap logger
122+
func NewLogr() logr.Logger {
123+
return zapr.NewLogger(zap.L())
141124
}
142125

143126
// Initialize creates and configures the appropriate logger.
144127
// If the UNSTRUCTURED_LOGS is set to true, it will output plain log message
145128
// with only time and LogLevelType (INFO, DEBUG, ERROR, WARN)).
146129
// Otherwise it will create a standard structured slog logger
147130
func Initialize() {
131+
var config zap.Config
148132
if unstructuredLogs() {
149-
w := os.Stderr
150-
151-
handler := tint.NewHandler(w, &tint.Options{
152-
Level: getLogLevel(),
153-
TimeFormat: time.Kitchen,
154-
})
155-
156-
slogger := slog.New(handler)
157-
158-
slog.SetDefault(slogger)
159-
log = &slogLogger{logger: slogger}
133+
config = zap.NewDevelopmentConfig()
134+
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
135+
config.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.Kitchen)
136+
config.OutputPaths = []string{"stderr"}
160137
} else {
161-
w := os.Stdout
162-
163-
handler := slog.NewJSONHandler(w, &slog.HandlerOptions{
164-
Level: getLogLevel(),
165-
})
166-
167-
slogger := slog.New(handler)
168-
169-
slog.SetDefault(slogger)
170-
log = &slogLogger{logger: slogger}
138+
config = zap.NewProductionConfig()
139+
config.OutputPaths = []string{"stdout"}
171140
}
172-
}
173141

174-
// GetLogger returns a context-specific logger
175-
func GetLogger(component string) Logger {
176-
if slogger, ok := log.(*slogLogger); ok {
177-
return &slogLogger{
178-
logger: slogger.logger.With("component", component),
179-
}
142+
// Set log level based on current debug flag
143+
if viper.GetBool("debug") {
144+
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
145+
} else {
146+
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
180147
}
181148

182-
return log
149+
zap.ReplaceGlobals(zap.Must(config.Build()))
183150
}
184151

185-
// getLogLevel returns the appropriate slog.Level based on the debug flag
186-
func getLogLevel() slog.Level {
187-
var level slog.Level
188-
if viper.GetBool("debug") {
189-
level = slog.LevelDebug
190-
} else {
191-
level = slog.LevelInfo
152+
func unstructuredLogs() bool {
153+
unstructuredLogs, err := strconv.ParseBool(os.Getenv("UNSTRUCTURED_LOGS"))
154+
if err != nil {
155+
// at this point if the error is not nil, the env var wasn't set, or is ""
156+
// which means we just default to outputting unstructured logs.
157+
return true
192158
}
193-
return level
159+
return unstructuredLogs
194160
}

0 commit comments

Comments
 (0)