Skip to content

Commit

Permalink
feat: add charmbracelet text logger as text logger
Browse files Browse the repository at this point in the history
  • Loading branch information
lvlcn-t committed Feb 22, 2024
1 parent d5e9904 commit 4901878
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 13 deletions.
16 changes: 16 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
module github.com/lvlcn-t/loggerhead

go 1.21

require github.com/charmbracelet/log v0.3.1

require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/sys v0.13.0 // indirect
)
35 changes: 35 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
12 changes: 6 additions & 6 deletions internal/logger/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ func (l *logger) FatalContext(ctx context.Context, msg string, args ...any) {

// getLevel takes a level string and maps it to the corresponding Level
// Returns the level if no mapped level is found it returns info level
func getLevel(level string) Level {
func getLevel(level string) int {
switch strings.ToUpper(level) {
case "DEBUG":
return slog.LevelDebug
return int(slog.LevelDebug)
case "INFO":
return slog.LevelInfo
return int(slog.LevelInfo)
case "WARN", "WARNING":
return slog.LevelWarn
return int(slog.LevelWarn)
case "ERROR":
return slog.LevelError
return int(slog.LevelError)
default:
return slog.LevelInfo
return int(slog.LevelInfo)
}
}
10 changes: 5 additions & 5 deletions internal/logger/extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (

func TestGetLevel(t *testing.T) {
tests := []struct {
name string
input string
expect Level
name string
input string
want Level
}{
{"Empty string", "", slog.LevelInfo},
{"Debug level", "DEBUG", slog.LevelDebug},
Expand All @@ -23,8 +23,8 @@ func TestGetLevel(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := getLevel(tt.input)
if got != tt.expect {
t.Errorf("getLevel(%s) = %v, want %v", tt.input, got, tt.expect)
if got != int(tt.want) {
t.Errorf("getLevel(%s) = %v, want %v", tt.input, got, tt.want)
}
})
}
Expand Down
27 changes: 26 additions & 1 deletion internal/logger/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
"log/slog"
"net/http"
"os"
"strings"
"time"

clog "github.com/charmbracelet/log"
)

type Logger interface {
Expand All @@ -29,6 +33,11 @@ type logger struct {
// If handlers are provided, the first handler in the slice is used; otherwise,
// a default JSON handler writing to os.Stderr is used. This function allows for
// custom configuration of logging handlers.
//
// Example:
//
// log := logger.NewLogger()
// log.Info("Hello, world!")
func NewLogger(h ...slog.Handler) Logger {
return &logger{
core: newCoreLogger(getHandler(h...)),
Expand Down Expand Up @@ -84,12 +93,28 @@ func Middleware(ctx context.Context) func(http.Handler) http.Handler {
}
}

// getHandler returns the first handler in the slice if it exists; otherwise, it returns a new base handler.
func getHandler(h ...slog.Handler) slog.Handler {
if len(h) > 0 {
return h[0]
}
return newBaseHandler()
}

// newBaseHandler returns a new slog.Handler based on the environment variables.
func newBaseHandler() slog.Handler {
l := getLevel(os.Getenv("LOG_LEVEL"))
if strings.ToUpper(os.Getenv("LOG_FORMAT")) == "TEXT" {
h := clog.New(os.Stderr)
h.SetTimeFormat(time.Kitchen)
h.SetReportTimestamp(true)
h.SetReportCaller(true)
h.SetLevel(clog.Level(l))
return h
}

return slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
AddSource: true,
Level: getLevel(os.Getenv("LOG_LEVEL")),
Level: slog.Level(l),
})
}
62 changes: 61 additions & 1 deletion internal/logger/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"os"
"reflect"
"testing"

clog "github.com/charmbracelet/log"
)

func TestNewLogger(t *testing.T) {
Expand Down Expand Up @@ -67,7 +69,7 @@ func TestNewLogger(t *testing.T) {

if tt.logLevelEnv != "" {
want := getLevel(tt.logLevelEnv)
got := log.Enabled(context.Background(), want)
got := log.Enabled(context.Background(), slog.Level(want))
if !got {
t.Errorf("Expected log level: %v", want)
}
Expand Down Expand Up @@ -182,3 +184,61 @@ func TestMiddleware(t *testing.T) {
})
}
}

func TestNewBaseHandler(t *testing.T) {
tests := []struct {
name string
format string
level string
wantLevel int
}{
{
name: "Default handler",
format: "",
level: "",
wantLevel: int(slog.LevelInfo),
},
{
name: "Text handler with custom log level",
format: "TEXT",
level: "DEBUG",
wantLevel: int(clog.DebugLevel),
},
{
name: "JSON handler with custom log level",
format: "JSON",
level: "WARN",
wantLevel: int(slog.LevelWarn),
},
{
name: "Invalid log level",
format: "TEXT",
level: "UNKNOWN",
wantLevel: int(clog.InfoLevel),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv("LOG_FORMAT", tt.format)
t.Setenv("LOG_LEVEL", tt.level)

handler := newBaseHandler()

if tt.format == "TEXT" {
if _, ok := handler.(*clog.Logger); !ok {
t.Errorf("Expected handler to be of type *log.Logger")
}
} else {
if _, ok := handler.(*slog.JSONHandler); !ok {
t.Errorf("Expected handler to be of type *slog.JSONHandler")
}
}

ok := handler.Enabled(context.Background(), slog.Level(tt.wantLevel))
if !ok {
t.Errorf("Expected log level: %v", tt.wantLevel)
}
})
}
}
7 changes: 7 additions & 0 deletions logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@ func TestFromContext(t *testing.T) {

logFromCtx.Info("Test")
}

func TestSomeLoggingWithEnv(t *testing.T) {
t.Setenv("LOG_LEVEL", "debug")
t.Setenv("LOG_FORMAT", "text")
log := logger.NewLogger()
log.Info("Test")
}

0 comments on commit 4901878

Please sign in to comment.