Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.25.4"
go-version: "1.25.5"

- name: Run tests
run: make test
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ compile:

.PHONY: test
test:
go test -cover -race -v ./...
go test -cover -race ./...

.PHONY: build
build:
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,23 @@ This will:
Then test it:

```bash
kubectl port-forward pod/mokv-0 9800:8400
kubectl port-forward pod/mokv-0 9800:8400
# In another terminal:
go run cmd/get_servers.go -addr localhost:9800
➜ go run cmd/test_kv.go -addr localhost:8400
Getting servers:
- mokv-0.mokv.default.svc.cluster.local:8400 -> is leader: true
- mokv-1.mokv.default.svc.cluster.local:8400 -> is leader: false
- mokv-2.mokv.default.svc.cluster.local:8400 -> is leader: false

Setting key 'hello' = 'world'
Set OK: true

Getting key 'hello'
Got: hello = world
```

```bash
kubectl scale statefulset mokv --replicas=5
```

### Configuration
Expand Down
4 changes: 2 additions & 2 deletions cmd/test_kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func main() {
}

// Set
fmt.Println("Setting key 'hello' = 'world'")
fmt.Println("\nSetting key 'hello' = 'world'")
setRes, err := client.Set(ctx, &api.SetRequest{Key: "hello", Value: []byte("world")})
if err != nil {
log.Fatal(err)
Expand All @@ -49,5 +49,5 @@ func main() {
if err != nil {
log.Fatal(err)
}
fmt.Printf("Got: %s = %s\n\n", getRes.Key, string(getRes.Value))
fmt.Printf("Got: %s = %s\n", getRes.Key, string(getRes.Value))
}
10 changes: 10 additions & 0 deletions discovery/mebership.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package discovery

import (
stdlog "log"
"net"

"github.com/dynamic-calm/mokv/logger"
"github.com/rs/zerolog/log"

"github.com/hashicorp/memberlist"
Expand Down Expand Up @@ -59,11 +61,19 @@ func (m *Membership) setupSerf() error {
if err != nil {
return err
}

// Setup logger
serfLogger := log.With().Str("component", "serf").Logger()
stdLogger := stdlog.New(logger.NewZeroLogWriter(serfLogger), "", 0)

// Serf config
config := serf.DefaultConfig()
config.Init()
config.Logger = stdLogger

// Memberlist config
mlConfig := memberlist.DefaultLocalConfig()
mlConfig.Logger = stdLogger
mlConfig.BindAddr = addr.IP.String()
mlConfig.BindPort = addr.Port
mlConfig.AdvertisePort = addr.Port
Expand Down
1 change: 1 addition & 0 deletions example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ rpc-port: 8400
metrics-port: 4000
start-join-addrs: []
bootstrap: true
log-level: "INFO"
1 change: 1 addition & 0 deletions example/config2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ metrics-port: 4003
start-join-addrs:
- "127.0.0.1:8401"
bootstrap: false
log-level: "INFO"
1 change: 1 addition & 0 deletions example/config3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ metrics-port: 4006
start-join-addrs:
- "127.0.0.1:8401"
bootstrap: false
log-level: "INFO"
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module github.com/dynamic-calm/mokv

go 1.25.4
go 1.25.5

require (
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/memberlist v0.5.3
github.com/hashicorp/raft v1.7.3
github.com/hashicorp/raft-boltdb/v2 v2.3.1
Expand Down Expand Up @@ -33,7 +33,6 @@ require (
github.com/google/btree v1.1.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-metrics v0.5.4 // indirect
github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect
Expand Down
69 changes: 0 additions & 69 deletions go.sum

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/dynamic-calm/mokv/api"
"github.com/dynamic-calm/mokv/logger"
"github.com/dynamic-calm/mokv/store"
"github.com/hashicorp/raft"
raftboltdb "github.com/hashicorp/raft-boltdb/v2"
Expand Down Expand Up @@ -294,6 +295,8 @@ func (kv *KV) setupRaft(dataDir string) error {
)

config := raft.DefaultConfig()
raftLogger := log.With().Str("component", "raft").Logger()
config.Logger = logger.NewHCLogWrapper(raftLogger)
config.LocalID = kv.cfg.Raft.LocalID
config.HeartbeatTimeout = 1 * time.Second
config.ElectionTimeout = 3 * time.Second
Expand Down
195 changes: 195 additions & 0 deletions logger/hclog_wrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Taken from https://github.com/psapezhka/hclog-zerolog/blob/main/wrapper.go
package logger

import (
"io"
"log"

"github.com/hashicorp/go-hclog"
"github.com/rs/zerolog"
)

// DefaultNameField — field [hclog.Logger] name will be written to.
//
// [hclog] has a concept of logger name and, moreover, name inheritance
// (when you create named logger on top of named logger).
// Logger name acts like a prefix for the log message.
// On the other hand, [zerolog] operates key/value pairs to add context to messages.
// So, we convert the [hclog] logger name to key/value context pair for [zerolog]
// This is a default, can be overridden while creating wrapper with the [NewWithCustomNameField]
const DefaultNameField = "hclog_name"

type Logger struct {
logger zerolog.Logger
nameField string
name string
}

// New creates an instance of [Logger] wrapping provided [zerolog.Logger].
//
// Example of wrapping the default global zerolog logger
// (using hashicorp/raft as an example of lib depending on [hclog.Logger])
//
// raftLogger := log.With().Str("component", "raft").Logger()
// config := raft.DefaultConfig()
// config.Logger = hclogzerolog.New(raftLogger)
//
// See:
// - https://pkg.go.dev/github.com/hashicorp/raft#Config
func NewHCLogWrapper(logger zerolog.Logger) *Logger {
return &Logger{
logger: logger.With().Str(DefaultNameField, "").Logger(),
nameField: DefaultNameField,
name: "",
}
}

// NewWithCustomNameField — does exactly the same as [New] but with the ability to set field (key)
// the [hclog.Logger] name will be written to.
func NewWithCustomNameField(logger zerolog.Logger, nameField string) *Logger {
return &Logger{
logger: logger.With().Str(nameField, "").Logger(),
nameField: nameField,
name: "",
}
}

func (l *Logger) Log(level hclog.Level, msg string, args ...any) {
switch level {
case hclog.Trace:
l.logger.Trace().Fields(args).Msg(msg)
case hclog.Debug:
l.logger.Debug().Fields(args).Msg(msg)
case hclog.Info:
l.logger.Info().Fields(args).Msg(msg)
case hclog.Warn:
l.logger.Warn().Fields(args).Msg(msg)
case hclog.Error:
l.logger.Error().Fields(args).Msg(msg)
case hclog.NoLevel:
l.logger.Log().Fields(args).Msg(msg)
case hclog.Off:
// no-op
default:
l.logger.Error().Msgf("Unknown log level: %s", level)
}
}

func (l *Logger) Trace(format string, args ...any) {
l.logger.Trace().Fields(args).Msg(format)
}

func (l *Logger) Debug(format string, args ...any) {
l.logger.Debug().Fields(args).Msg(format)
}

func (l *Logger) Info(format string, args ...any) {
l.logger.Info().Fields(args).Msg(format)
}

func (l *Logger) Warn(format string, args ...any) {
l.logger.Warn().Fields(args).Msg(format)
}

func (l *Logger) Error(format string, args ...any) {
l.logger.Error().Fields(args).Msg(format)
}

func (l *Logger) IsTrace() bool {
return l.logger.GetLevel() == zerolog.TraceLevel
}

func (l *Logger) IsDebug() bool {
return l.logger.GetLevel() == zerolog.DebugLevel
}

func (l *Logger) IsInfo() bool {
return l.logger.GetLevel() == zerolog.InfoLevel
}

func (l *Logger) IsWarn() bool {
return l.logger.GetLevel() == zerolog.WarnLevel
}

func (l *Logger) IsError() bool {
return l.logger.GetLevel() == zerolog.ErrorLevel
}

func (l *Logger) ImpliedArgs() []any {
return nil
}

func (l *Logger) With(args ...any) hclog.Logger {
return &Logger{l.logger.With().Fields(args).Logger(), l.nameField, l.name}
}

func (l *Logger) Name() string {
return l.name
}

func (l *Logger) Named(name string) hclog.Logger {
var newName string
if l.name == "" {
newName = name
} else {
newName = l.name + "." + name
}

return &Logger{l.logger.With().Str(l.nameField, newName).Logger(), l.nameField, newName}
}

func (l *Logger) ResetNamed(name string) hclog.Logger {
return &Logger{l.logger.With().Str(l.nameField, name).Logger(), l.nameField, name}
}

func (l *Logger) SetLevel(level hclog.Level) {
switch level {
case hclog.Trace:
l.logger = l.logger.Level(zerolog.TraceLevel)
case hclog.Debug:
l.logger = l.logger.Level(zerolog.DebugLevel)
case hclog.Info:
l.logger = l.logger.Level(zerolog.InfoLevel)
case hclog.Warn:
l.logger = l.logger.Level(zerolog.WarnLevel)
case hclog.Error:
l.logger = l.logger.Level(zerolog.ErrorLevel)
case hclog.Off:
l.logger = l.logger.Level(zerolog.Disabled)
case hclog.NoLevel:
l.logger = l.logger.Level(zerolog.NoLevel)
default:
l.logger.Error().Msgf("Unknown log level: %s", level)
}
}

func (l *Logger) GetLevel() hclog.Level {
switch l.logger.GetLevel() {
case zerolog.TraceLevel:
return hclog.Trace
case zerolog.DebugLevel:
return hclog.Debug
case zerolog.InfoLevel:
return hclog.Info
case zerolog.WarnLevel:
return hclog.Warn
case zerolog.ErrorLevel, zerolog.FatalLevel, zerolog.PanicLevel:
return hclog.Error
case zerolog.Disabled:
return hclog.Off
case zerolog.NoLevel:
return hclog.NoLevel
default:
l.logger.Error().Msgf("Unknown log level: %s", l.logger.GetLevel())

return hclog.NoLevel
}
}

func (l *Logger) StandardLogger(_ *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.logger, "", 0)
}

func (l *Logger) StandardWriter(_ *hclog.StandardLoggerOptions) io.Writer {
return l.logger
}
Loading