Skip to content

Commit c56df44

Browse files
authored
feat: switch logging to slog (#328)
Signed-off-by: Ales Verbic <[email protected]>
1 parent 87ffa46 commit c56df44

File tree

5 files changed

+98
-86
lines changed

5 files changed

+98
-86
lines changed

cmd/cardano-node-api/main.go

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,40 +52,29 @@ func main() {
5252
}
5353

5454
// Configure logging
55-
logging.Setup(&cfg.Logging)
55+
logging.Configure()
5656
logger := logging.GetLogger()
57-
// Sync logger on exit
58-
defer func() {
59-
if err := logger.Sync(); err != nil {
60-
// We don't actually care about the error here, but we have to do something
61-
// to appease the linter
62-
return
63-
}
64-
}()
6557

6658
// Test node connection
6759
if cfg.Node.SkipCheck {
68-
logger.Debugf("skipping node check")
60+
logger.Debug("skipping node check")
6961
} else {
7062
if oConn, err := node.GetConnection(nil); err != nil {
71-
logger.Fatalf("failed to connect to node: %s", err)
63+
logger.Error("failed to connect to node:", "error", err)
7264
} else {
7365
oConn.Close()
7466
}
7567
}
7668

77-
logger.Infof(
78-
"starting cardano-node-api version %s",
79-
version.GetVersionString(),
80-
)
69+
logger.Info("starting cardano-node-api version", "version", version.GetVersionString())
8170

8271
// Start debug listener
8372
if cfg.Debug.ListenPort > 0 {
84-
logger.Infof(
73+
logger.Info(fmt.Sprintf(
8574
"starting debug listener on %s:%d",
8675
cfg.Debug.ListenAddress,
8776
cfg.Debug.ListenPort,
88-
)
77+
))
8978
go func() {
9079
err := http.ListenAndServe(
9180
fmt.Sprintf(
@@ -96,21 +85,24 @@ func main() {
9685
nil,
9786
)
9887
if err != nil {
99-
logger.Fatalf("failed to start debug listener: %s", err)
88+
logger.Error("failed to start debug listener:", "error", err)
89+
os.Exit(1)
10090
}
10191
}()
10292
}
10393

10494
// Start API listener
10595
go func() {
10696
if err := api.Start(cfg); err != nil {
107-
logger.Fatalf("failed to start API: %s", err)
97+
logger.Error("failed to start API:", "error", err)
98+
os.Exit(1)
10899
}
109100
}()
110101

111102
// Start UTxO RPC gRPC listener
112103
if err := utxorpc.Start(cfg); err != nil {
113-
logger.Fatalf("failed to start gRPC: %s", err)
104+
logger.Error("failed to start gRPC:", "error", err)
105+
os.Exit(1)
114106
}
115107

116108
// Wait forever

internal/api/api.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ package api
1616

1717
import (
1818
"fmt"
19-
"time"
19+
"os"
2020

2121
"github.com/blinklabs-io/cardano-node-api/internal/config"
2222
"github.com/blinklabs-io/cardano-node-api/internal/logging"
2323

24-
ginzap "github.com/gin-contrib/zap"
2524
"github.com/gin-gonic/gin"
2625
"github.com/penglongli/gin-metrics/ginmetrics"
2726

@@ -44,17 +43,17 @@ func Start(cfg *config.Config) error {
4443
// Standard logging
4544
logger := logging.GetLogger()
4645
if cfg.Tls.CertFilePath != "" && cfg.Tls.KeyFilePath != "" {
47-
logger.Infof(
46+
logger.Info(fmt.Sprintf(
4847
"starting API TLS listener on %s:%d",
4948
cfg.Api.ListenAddress,
5049
cfg.Api.ListenPort,
51-
)
50+
))
5251
} else {
53-
logger.Infof(
52+
logger.Info(fmt.Sprintf(
5453
"starting API listener on %s:%d",
5554
cfg.Api.ListenAddress,
5655
cfg.Api.ListenPort,
57-
)
56+
))
5857
}
5958
// Disable gin debug and color output
6059
gin.SetMode(gin.ReleaseMode)
@@ -69,14 +68,15 @@ func Start(cfg *config.Config) error {
6968
skipPaths := []string{}
7069
if cfg.Logging.Healthchecks {
7170
skipPaths = append(skipPaths, "/healthcheck")
72-
logger.Infof("disabling access logs for /healthcheck")
71+
logger.Info("disabling access logs for /healthcheck")
7372
}
74-
router.Use(ginzap.GinzapWithConfig(accessLogger, &ginzap.Config{
75-
TimeFormat: time.RFC3339,
76-
UTC: true,
77-
SkipPaths: skipPaths,
78-
}))
79-
router.Use(ginzap.RecoveryWithZap(accessLogger, true))
73+
accessMiddleware := func(c *gin.Context) {
74+
accessLogger.Info("request received", "method", c.Request.Method, "path", c.Request.URL.Path, "remote_addr", c.ClientIP())
75+
c.Next()
76+
statusCode := c.Writer.Status()
77+
accessLogger.Info("response sent", "status", statusCode, "method", c.Request.Method, "path", c.Request.URL.Path, "remote_addr", c.ClientIP())
78+
}
79+
router.Use(accessMiddleware)
8080

8181
// Create a healthcheck
8282
router.GET("/healthcheck", handleHealthcheck)
@@ -104,14 +104,15 @@ func Start(cfg *config.Config) error {
104104
// Start metrics listener
105105
go func() {
106106
// TODO: return error if we cannot initialize metrics
107-
logger.Infof("starting metrics listener on %s:%d",
107+
logger.Info(fmt.Sprintf("starting metrics listener on %s:%d",
108108
cfg.Metrics.ListenAddress,
109-
cfg.Metrics.ListenPort)
109+
cfg.Metrics.ListenPort))
110110
err := metricsRouter.Run(fmt.Sprintf("%s:%d",
111111
cfg.Metrics.ListenAddress,
112112
cfg.Metrics.ListenPort))
113113
if err != nil {
114-
logger.Fatalf("failed to start metrics listener: %s", err)
114+
logger.Error("failed to start metrics listener:", "error", err)
115+
os.Exit(1)
115116
}
116117
}()
117118

internal/api/localtxsubmission.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func handleLocalSubmitTx(c *gin.Context) {
5050
// Check our headers for content-type
5151
if c.ContentType() != "application/cbor" {
5252
// Log the error, return an error to the user, and increment failed count
53-
logger.Errorf("invalid request body, should be application/cbor")
53+
logger.Error("invalid request body, should be application/cbor")
5454
c.JSON(415, "invalid request body, should be application/cbor")
5555
// _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
5656
return
@@ -59,15 +59,15 @@ func handleLocalSubmitTx(c *gin.Context) {
5959
txRawBytes, err := io.ReadAll(c.Request.Body)
6060
if err != nil {
6161
// Log the error, return an error to the user, and increment failed count
62-
logger.Errorf("failed to read request body: %s", err)
62+
logger.Error("failed to read request body:", "error", err)
6363
c.JSON(500, "failed to read request body")
6464
// _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
6565
return
6666
}
6767
// Close request body after read
6868
if c.Request.Body != nil {
6969
if err = c.Request.Body.Close(); err != nil {
70-
logger.Errorf("failed to close request body: %s", err)
70+
logger.Error("failed to close request body:", "error", err)
7171
}
7272
}
7373
// Send TX
@@ -99,7 +99,7 @@ func handleLocalSubmitTx(c *gin.Context) {
9999
go func() {
100100
err, ok := <-errorChan
101101
if ok {
102-
logger.Errorf("failure communicating with node: %s", err)
102+
logger.Error("failure communicating with node:", "error", err)
103103
c.JSON(500, "failure communicating with node")
104104
// _ = ginmetrics.GetMonitor().GetMetric("tx_submit_fail_count").Inc(nil)
105105
}

internal/logging/logging.go

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 Blink Labs Software
1+
// Copyright 2024 Blink Labs Software
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -15,56 +15,75 @@
1515
package logging
1616

1717
import (
18-
"github.com/blinklabs-io/cardano-node-api/internal/config"
19-
"go.uber.org/zap"
20-
"go.uber.org/zap/zapcore"
21-
"log"
18+
"log/slog"
19+
"os"
2220
"time"
23-
)
24-
25-
type Logger = zap.SugaredLogger
2621

27-
var globalLogger *Logger
22+
"github.com/blinklabs-io/cardano-node-api/internal/config"
23+
)
2824

29-
func Setup(cfg *config.LoggingConfig) {
30-
// Build our custom logging config
31-
loggerConfig := zap.NewProductionConfig()
32-
// Change timestamp key name
33-
loggerConfig.EncoderConfig.TimeKey = "timestamp"
34-
// Use a human readable time format
35-
loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(
36-
time.RFC3339,
37-
)
25+
var globalLogger *slog.Logger
26+
var accessLogger *slog.Logger
3827

39-
// Set level
40-
if cfg.Level != "" {
41-
level, err := zapcore.ParseLevel(cfg.Level)
42-
if err != nil {
43-
log.Fatalf("error configuring logger: %s", err)
44-
}
45-
loggerConfig.Level.SetLevel(level)
28+
// Configure initializes the global logger.
29+
func Configure() {
30+
cfg := config.GetConfig()
31+
var level slog.Level
32+
switch cfg.Logging.Level {
33+
case "debug":
34+
level = slog.LevelDebug
35+
case "info":
36+
level = slog.LevelInfo
37+
case "warn":
38+
level = slog.LevelWarn
39+
case "error":
40+
level = slog.LevelError
41+
default:
42+
level = slog.LevelInfo
4643
}
4744

48-
// Create the logger
49-
l, err := loggerConfig.Build()
50-
if err != nil {
51-
log.Fatal(err)
52-
}
45+
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
46+
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
47+
if a.Key == slog.TimeKey {
48+
return slog.String(
49+
"timestamp",
50+
a.Value.Time().Format(time.RFC3339),
51+
)
52+
}
53+
return a
54+
},
55+
Level: level,
56+
})
57+
globalLogger = slog.New(handler).With("component", "main")
5358

54-
// Store the "sugared" version of the logger
55-
globalLogger = l.Sugar()
59+
// Configure access logger
60+
accessHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
61+
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
62+
if a.Key == slog.TimeKey {
63+
return slog.String(
64+
"timestamp",
65+
a.Value.Time().Format(time.RFC3339),
66+
)
67+
}
68+
return a
69+
},
70+
Level: slog.LevelInfo,
71+
})
72+
accessLogger = slog.New(accessHandler).With("component", "access")
5673
}
5774

58-
func GetLogger() *zap.SugaredLogger {
75+
// GetLogger returns the global application logger.
76+
func GetLogger() *slog.Logger {
77+
if globalLogger == nil {
78+
Configure()
79+
}
5980
return globalLogger
6081
}
6182

62-
func GetDesugaredLogger() *zap.Logger {
63-
return globalLogger.Desugar()
64-
}
65-
66-
func GetAccessLogger() *zap.Logger {
67-
return globalLogger.Desugar().
68-
With(zap.String("type", "access")).
69-
WithOptions(zap.WithCaller(false))
83+
// GetAccessLogger returns the access logger.
84+
func GetAccessLogger() *slog.Logger {
85+
if accessLogger == nil {
86+
Configure()
87+
}
88+
return accessLogger
7089
}

internal/utxorpc/api.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ func Start(cfg *config.Config) error {
3636
// Standard logging
3737
logger := logging.GetLogger()
3838
if cfg.Tls.CertFilePath != "" && cfg.Tls.KeyFilePath != "" {
39-
logger.Infof(
40-
"starting gRPC TLS listener on %s:%d",
39+
logger.Info(fmt.Sprintf(
40+
"starting gRPC TLS listener on: %s:%d",
4141
cfg.Utxorpc.ListenAddress,
4242
cfg.Utxorpc.ListenPort,
43-
)
43+
))
4444
} else {
45-
logger.Infof(
45+
logger.Info(fmt.Sprintf(
4646
"starting gRPC listener on %s:%d",
4747
cfg.Utxorpc.ListenAddress,
4848
cfg.Utxorpc.ListenPort,
49-
)
49+
))
5050
}
5151
mux := http.NewServeMux()
5252
compress1KB := connect.WithCompressMinBytes(1024)

0 commit comments

Comments
 (0)