Skip to content

Commit

Permalink
feat: switch logging to slog (#120)
Browse files Browse the repository at this point in the history
Signed-off-by: Ales Verbic <verbotenj@blinklabs.io>
  • Loading branch information
verbotenj authored Dec 31, 2024
1 parent c14bf38 commit 69388d4
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 81 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,34 @@
</div>

Programmatic Cardano Wallet

Start a Bursa wallet and interact with it using the Bursa API.

```golang
# Clone the Bursa repository
git clone git@github.com:blinklabs-io/bursa.git
cd bursa

# Start the Bursa API server
go run ./cmd/bursa api
```

Access API Swagger documentation: [http://localhost:8080/swagger/index.html](http://localhost:8080/swagger/index.html)

For more information about Bursa CLI

```bash
go run ./cmd/bursa
Usage:
bursa [command]

Available Commands:
api Runs the api
help Help about any command
wallet Wallet commands

Flags:
-h, --help help for bursa

Use "bursa [command] --help" for more information about a command.
```
13 changes: 6 additions & 7 deletions cmd/bursa/api.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Blink Labs Software
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,8 @@
package main

import (
"os"

"github.com/blinklabs-io/bursa/internal/api"
"github.com/blinklabs-io/bursa/internal/config"
"github.com/blinklabs-io/bursa/internal/logging"
Expand All @@ -30,13 +32,10 @@ func apiCommand() *cobra.Command {
// Start API listener
logger := logging.GetLogger()
// Start API listener
logger.Infof(
"starting API listener on %s:%d",
cfg.Api.ListenAddress,
cfg.Api.ListenPort,
)
logger.Info("starting API listener on", "address", cfg.Api.ListenAddress, "port", cfg.Api.ListenPort)
if err := api.Start(cfg); err != nil {
logger.Fatalf("failed to start API: %s", err)
logger.Error("failed to start API:", "error", err)
os.Exit(1)
}

// Wait forever
Expand Down
11 changes: 2 additions & 9 deletions cmd/bursa/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Blink Labs Software
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,14 +27,7 @@ const (

func main() {
// Configure logging
logging.Setup()
logger := logging.GetLogger()
defer func() {
if err := logger.Sync(); err != nil {
// ignore error
return
}
}()
logging.Configure()

rootCmd := &cobra.Command{
Use: programName,
Expand Down
27 changes: 11 additions & 16 deletions internal/api/api.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Blink Labs Software
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -17,9 +17,6 @@ package api
import (
"fmt"
"net/http"
"time"

ginzap "github.com/gin-contrib/zap"

"github.com/gin-gonic/gin"
"github.com/penglongli/gin-metrics/ginmetrics"
Expand Down Expand Up @@ -62,12 +59,13 @@ func Start(cfg *config.Config) error {
logger := logging.GetLogger()
// Access logging
accessLogger := logging.GetAccessLogger()
router.Use(ginzap.GinzapWithConfig(accessLogger, &ginzap.Config{
TimeFormat: time.RFC3339,
UTC: true,
SkipPaths: []string{},
}))
router.Use(ginzap.RecoveryWithZap(accessLogger, true))
accessMiddleware := func(c *gin.Context) {
accessLogger.Info("request received", "method", c.Request.Method, "path", c.Request.URL.Path, "remote_addr", c.ClientIP())
c.Next()
statusCode := c.Writer.Status()
accessLogger.Info("response sent", "status", statusCode, "method", c.Request.Method, "path", c.Request.URL.Path, "remote_addr", c.ClientIP())
}
router.Use(accessMiddleware)

// Create a healthcheck
router.GET("/healthcheck", handleHealthcheck)
Expand Down Expand Up @@ -111,10 +109,7 @@ func Start(cfg *config.Config) error {
// Start metrics listener
go func() {
// TODO: return error if we cannot initialize metrics
logger.Infof("starting metrics listener on %s:%d",
cfg.Metrics.ListenAddress,
cfg.Metrics.ListenPort,
)
logger.Info("starting metrics listener", "address", cfg.Metrics.ListenAddress, ":", cfg.Metrics.ListenPort)
_ = metricsRouter.Run(fmt.Sprintf("%s:%d",
cfg.Metrics.ListenAddress,
cfg.Metrics.ListenPort,
Expand Down Expand Up @@ -149,7 +144,7 @@ func handleWalletCreate(c *gin.Context) {

mnemonic, err := bursa.NewMnemonic()
if err != nil {
logger.Errorf("failed to load mnemonic: %s", err)
logger.Error("failed to load mnemonic", "error", err)
c.JSON(500, fmt.Sprintf("failed to load mnemonic: %s", err))
_ = ginmetrics.GetMonitor().
GetMetric("bursa_wallets_fail_count").
Expand All @@ -159,7 +154,7 @@ func handleWalletCreate(c *gin.Context) {

w, err := bursa.NewDefaultWallet(mnemonic)
if err != nil {
logger.Errorf("failed to initialize wallet: %s", err)
logger.Error("failed to initialize wallet", "error", err)
c.JSON(500, fmt.Sprintf("failed to initialize wallet: %s", err))
_ = ginmetrics.GetMonitor().
GetMetric("bursa_wallets_fail_count").
Expand Down
14 changes: 14 additions & 0 deletions internal/api/api_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
Expand Down
16 changes: 8 additions & 8 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Blink Labs Software
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,20 +35,21 @@ func Run(output string) {
if mnemonic == "" {
mnemonic, err = bursa.NewMnemonic()
if err != nil {
logger.Fatalf("failed to load mnemonic: %s", err)
logger.Error("failed to load mnemonic", "error", err)
os.Exit(1)
}
}
w, err := bursa.NewDefaultWallet(mnemonic)
if err != nil {
logger.Fatalf("failed to initialize wallet: %s", err)
logger.Error("failed to initialize wallet", "error", err)
os.Exit(1)
}
if w == nil {
logger.Fatalf("wallet empty after init... this shouldn't happen")
logger.Error("wallet empty after init... this shouldn't happen")
os.Exit(1)
}

logger.Info("Loaded mnemonic and generated address...")
logger.Info("Loaded mnemonic and generated address")

if output == "" {
fmt.Printf("MNEMONIC=%s\n", w.Mnemonic)
Expand Down Expand Up @@ -104,10 +105,9 @@ func Run(output string) {
}
err = g.Wait()
if err != nil {
logger.Fatalf("error occurred: %s", err)
logger.Error("error occurred", "error", err)
os.Exit(1)
}
logger.Infof("wrote output files to %s", output)

logger.Info("wrote output files", "directory", output)
}
}
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Blink Labs Software
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
109 changes: 70 additions & 39 deletions internal/logging/logging.go
Original file line number Diff line number Diff line change
@@ -1,58 +1,89 @@
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package logging

import (
"log"
"log/slog"
"os"
"time"

"github.com/blinklabs-io/bursa/internal/config"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

type Logger = zap.SugaredLogger

var globalLogger *Logger
var globalLogger *slog.Logger
var accessLogger *slog.Logger

func Setup() {
// Configure initializes the global logger.
func Configure() {
cfg := config.GetConfig()
// Build our custom logging config
loggerConfig := zap.NewProductionConfig()
// Change timestamp key name
loggerConfig.EncoderConfig.TimeKey = "timestamp"
// Use a human readable time format
loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(
time.RFC3339,
)

// Set level
if cfg.Logging.Level != "" {
level, err := zapcore.ParseLevel(cfg.Logging.Level)
if err != nil {
log.Fatalf("error configuring logger: %s", err)
}
loggerConfig.Level.SetLevel(level)
var level slog.Level
switch cfg.Logging.Level {
case "debug":
level = slog.LevelDebug
case "info":
level = slog.LevelInfo
case "warn":
level = slog.LevelWarn
case "error":
level = slog.LevelError
default:
level = slog.LevelInfo
}

// Create the logger
l, err := loggerConfig.Build()
if err != nil {
log.Fatal(err)
}
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
return slog.String(
"timestamp",
a.Value.Time().Format(time.RFC3339),
)
}
return a
},
Level: level,
})
globalLogger = slog.New(handler).With("component", "main")

// Store the "sugared" version of the logger
globalLogger = l.Sugar()
// Configure access logger
accessHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
return slog.String(
"timestamp",
a.Value.Time().Format(time.RFC3339),
)
}
return a
},
Level: slog.LevelInfo,
})
accessLogger = slog.New(accessHandler).With("component", "access")
}

func GetLogger() *Logger {
// GetLogger returns the global application logger.
func GetLogger() *slog.Logger {
if globalLogger == nil {
Configure()
}
return globalLogger
}

func GetDesugaredLogger() *zap.Logger {
return globalLogger.Desugar()
}

func GetAccessLogger() *zap.Logger {
return globalLogger.Desugar().
With(zap.String("type", "access")).
WithOptions(zap.WithCaller(false))
// GetAccessLogger returns the access logger.
func GetAccessLogger() *slog.Logger {
if accessLogger == nil {
Configure()
}
return accessLogger
}
2 changes: 1 addition & 1 deletion internal/version/version.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Blink Labs Software
// Copyright 2024 Blink Labs Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down

0 comments on commit 69388d4

Please sign in to comment.