From 9dde8832ac5467cc86d6720b9ecb5a4c02aaaa29 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 16 Oct 2024 18:52:46 +0200 Subject: [PATCH] fix: use zap logger Signed-off-by: Valery Piashchynski --- .golangci.yml | 3 +- builder/builder.go | 30 +++---- builder/builder_test.go | 5 +- github/pool.go | 24 +++--- github/repo.go | 22 ++--- gitlab/repo.go | 14 ++-- go.mod | 5 +- go.sum | 6 +- internal/cli/build/build.go | 18 ++-- internal/cli/root.go | 10 ++- logger/logger.go | 162 ++++++++++++++++++++++++++++-------- 11 files changed, 198 insertions(+), 101 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 66bb5ed..dd99806 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,6 @@ # Documentation: run: - go: '1.17' timeout: 1m allow-parallel-runners: true @@ -36,7 +35,7 @@ linters: # All available linters list: ") if len(split) != 2 { - b.log.Error("not enough split args", slog.String("replace", replaces[i][0])) + b.log.Error("not enough split args", zap.String("replace", replaces[i][0])) continue } diff --git a/builder/builder_test.go b/builder/builder_test.go index 94f68a1..1ce0229 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -1,12 +1,12 @@ package builder import ( - "log/slog" "os" "path" "testing" "github.com/roadrunner-server/velox/v2024" + "go.uber.org/zap" ) const ( @@ -81,7 +81,8 @@ func setup() *Builder { "dummy_multiple_absolute_remote": []byte(replaceGoModMultipleRemote), } - b := NewBuilder("/tmp", []*velox.ModulesInfo{}, "", "v2024.1.0", false, slog.Default()) + l, _ := zap.NewDevelopment() + b := NewBuilder("/tmp", []*velox.ModulesInfo{}, "", "v2024.1.0", false, l) b.modules = []*velox.ModulesInfo{ { diff --git a/github/pool.go b/github/pool.go index a6c494e..eafe7c9 100644 --- a/github/pool.go +++ b/github/pool.go @@ -5,7 +5,6 @@ import ( "context" "errors" "fmt" - "log/slog" "net/http" "path" "strings" @@ -14,6 +13,7 @@ import ( "github.com/google/go-github/v61/github" "github.com/roadrunner-server/velox/v2024" + "go.uber.org/zap" ) const ( @@ -26,7 +26,7 @@ type processor struct { errs []error wg sync.WaitGroup mu sync.Mutex - log *slog.Logger + log *zap.Logger queueCh chan *pcfg modinfo []*velox.ModulesInfo client *github.Client @@ -37,7 +37,7 @@ type pcfg struct { name string } -func newPool(log *slog.Logger, client *github.Client) *processor { +func newPool(log *zap.Logger, client *github.Client) *processor { p := &processor{ maxWorkers: 10, log: log, @@ -61,11 +61,11 @@ func (p *processor) run() { for v := range p.queueCh { modInfo := new(velox.ModulesInfo) p.log.Debug("fetching plugin data", - slog.String("repository", v.pluginCfg.Repo), - slog.String("owner", v.pluginCfg.Owner), - slog.String("folder", v.pluginCfg.Folder), - slog.String("plugin", v.name), - slog.String("ref", v.pluginCfg.Ref), + zap.String("repository", v.pluginCfg.Repo), + zap.String("owner", v.pluginCfg.Owner), + zap.String("folder", v.pluginCfg.Folder), + zap.String("plugin", v.name), + zap.String("ref", v.pluginCfg.Ref), ) if v.pluginCfg.Ref == "" { @@ -93,7 +93,7 @@ func (p *processor) run() { line := scanner.Text() switch { //nolint:gocritic case strings.HasPrefix(line, modLine): - p.log.Debug("reading module info", slog.String("plugin", v.name), slog.String("module", line)) + p.log.Debug("reading module info", zap.String("plugin", v.name), zap.String("module", line)) // module github.com/roadrunner-server/logger/v2, we split and get the second part retMod := strings.Split(line, " ") @@ -115,10 +115,10 @@ func (p *processor) run() { err = resp.Body.Close() if err != nil { - p.log.Warn("failed to close response body, continuing", slog.Any("error", err)) + p.log.Warn("failed to close response body, continuing", zap.Any("error", err)) } - p.log.Debug("requesting commit", slog.String("plugin", v.name), slog.String("ref", v.pluginCfg.Ref)) + p.log.Debug("requesting commit", zap.String("plugin", v.name), zap.String("ref", v.pluginCfg.Ref)) commits, rsp, err := p.client.Repositories.ListCommits(context.Background(), v.pluginCfg.Owner, v.pluginCfg.Repo, &github.CommitsListOptions{ SHA: v.pluginCfg.Ref, Until: time.Now(), @@ -155,7 +155,7 @@ func (p *processor) run() { if v.pluginCfg.Replace != "" { modInfo.Replace = v.pluginCfg.Replace - p.log.Debug("found replace, applying", slog.String("plugin", v.name), slog.String("path", v.pluginCfg.Replace)) + p.log.Debug("found replace, applying", zap.String("plugin", v.name), zap.String("path", v.pluginCfg.Replace)) } p.mu.Lock() diff --git a/github/repo.go b/github/repo.go index 0e71369..030d9ce 100644 --- a/github/repo.go +++ b/github/repo.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "log/slog" "net/http" "os" "path" @@ -16,6 +15,7 @@ import ( "github.com/google/go-github/v61/github" "github.com/roadrunner-server/velox/v2024" + "go.uber.org/zap" "golang.org/x/oauth2" ) @@ -31,10 +31,10 @@ GHRepo represents template repository type GHRepo struct { client *github.Client config *velox.Config - log *slog.Logger + log *zap.Logger } -func NewGHRepoInfo(cfg *velox.Config, log *slog.Logger) *GHRepo { +func NewGHRepoInfo(cfg *velox.Config, log *zap.Logger) *GHRepo { var client *http.Client // if a token exists, use it to increase rate limiter @@ -53,7 +53,7 @@ func NewGHRepoInfo(cfg *velox.Config, log *slog.Logger) *GHRepo { // DownloadTemplate downloads template repository -> func (r *GHRepo) DownloadTemplate(tmp, version string) (string, error) { //nolint:gocyclo - r.log.Info("obtaining link", slog.String("owner", rrOwner), slog.String("repository", rrRepo), slog.String("encoding", "zip"), slog.String("ref", version)) + r.log.Info("obtaining link", zap.String("owner", rrOwner), zap.String("repository", rrRepo), zap.String("encoding", "zip"), zap.String("ref", version)) url, resp, err := r.client.Repositories.GetArchiveLink(context.Background(), rrOwner, rrRepo, github.Zipball, &github.RepositoryContentGetOptions{Ref: version}, 10) if err != nil { return "", err @@ -63,14 +63,14 @@ func (r *GHRepo) DownloadTemplate(tmp, version string) (string, error) { //nolin return "", fmt.Errorf("wrong response status, got: %d", resp.StatusCode) } - r.log.Info("seding download request", slog.String("url", url.String())) + r.log.Info("seding download request", zap.String("url", url.String())) request, err := r.client.NewRequest(http.MethodGet, url.String(), nil) if err != nil { return "", err } buf := new(bytes.Buffer) - r.log.Info("downloading repository", slog.String("url", url.String())) + r.log.Info("downloading repository", zap.String("url", url.String())) do, err := r.client.Do(context.Background(), request, buf) if err != nil { return "", err @@ -85,7 +85,7 @@ func (r *GHRepo) DownloadTemplate(tmp, version string) (string, error) { //nolin name := path.Join(tmp, "roadrunner-server-"+version) _ = os.RemoveAll(name) - r.log.Debug("saving repository in temporary folder", slog.String("path", name+zipExt)) + r.log.Debug("saving repository in temporary folder", zap.String("path", name+zipExt)) f, err := os.Create(name + zipExt) if err != nil { return "", err @@ -100,7 +100,7 @@ func (r *GHRepo) DownloadTemplate(tmp, version string) (string, error) { //nolin return "", err } - r.log.Debug("repository saved", slog.Int("bytes written", n)) + r.log.Debug("repository saved", zap.Int("bytes written", n)) rc, err := zip.OpenReader(name + zipExt) if err != nil { @@ -146,14 +146,14 @@ func (r *GHRepo) DownloadTemplate(tmp, version string) (string, error) { //nolin } for _, zf := range rc.File { - r.log.Debug("extracting repository", slog.String("file", zf.Name), slog.String("path", dest)) + r.log.Debug("extracting repository", zap.String("file", zf.Name), zap.String("path", dest)) err = extract(dest, zf) if err != nil { return "", err } } - r.log.Info("repository saved", slog.String("path", filepath.Join(dest, outDir))) //nolint:gosec + r.log.Info("repository saved", zap.String("path", filepath.Join(dest, outDir))) //nolint:gosec // first name is the output path return filepath.Join(dest, outDir), nil //nolint:gosec } @@ -201,7 +201,7 @@ func extract(dest string, zf *zip.File) error { // https://github.com/spiral/roadrunner-binary/archive/refs/tags/v2.7.0.zip func (r *GHRepo) GetPluginsModData() ([]*velox.ModulesInfo, error) { - poolExecutor := newPool(r.log, r.client) + poolExecutor := newPool(r.log.Named("pool"), r.client) for k, v := range r.config.GitHub.Plugins { poolExecutor.add(&pcfg{ pluginCfg: v, diff --git a/gitlab/repo.go b/gitlab/repo.go index 813f897..b4e06e7 100644 --- a/gitlab/repo.go +++ b/gitlab/repo.go @@ -6,7 +6,6 @@ import ( "encoding/base64" "errors" "fmt" - "log/slog" "net/http" "path" "strings" @@ -14,6 +13,7 @@ import ( "github.com/roadrunner-server/velox/v2024" "github.com/xanzy/go-gitlab" + "go.uber.org/zap" ) /* @@ -22,10 +22,10 @@ GLRepo represents template repository type GLRepo struct { client *gitlab.Client config *velox.Config - log *slog.Logger + log *zap.Logger } -func NewGLRepoInfo(cfg *velox.Config, log *slog.Logger) (*GLRepo, error) { +func NewGLRepoInfo(cfg *velox.Config, log *zap.Logger) (*GLRepo, error) { glc, err := gitlab.NewClient(cfg.GitLab.Token.Token, gitlab.WithBaseURL(cfg.GitLab.BaseURL.BaseURL)) if err != nil { return nil, err @@ -43,7 +43,7 @@ func (r *GLRepo) GetPluginsModData() ([]*velox.ModulesInfo, error) { for k, v := range r.config.GitLab.Plugins { modInfo := new(velox.ModulesInfo) - r.log.Debug("fetchin plugin data", slog.String("repository", v.Repo), slog.String("owner", v.Owner), slog.String("folder", v.Folder), slog.String("plugin", k), slog.String("ref", v.Ref)) + r.log.Debug("fetchin plugin data", zap.String("repository", v.Repo), zap.String("owner", v.Owner), zap.String("folder", v.Folder), zap.String("plugin", k), zap.String("ref", v.Ref)) if v.Ref == "" { return nil, errors.New("ref can't be empty") @@ -70,7 +70,7 @@ func (r *GLRepo) GetPluginsModData() ([]*velox.ModulesInfo, error) { scanner.Scan() ret := scanner.Text() - r.log.Debug("reading module info", slog.String("plugin", k), slog.String("mod", ret)) + r.log.Debug("reading module info", zap.String("plugin", k), zap.String("mod", ret)) // module github.com/roadrunner-server/logger/v2, we split and get the second part retMod := strings.Split(ret, " ") @@ -85,7 +85,7 @@ func (r *GLRepo) GetPluginsModData() ([]*velox.ModulesInfo, error) { modInfo.ModuleName = strings.TrimRight(retMod[1], "\n") - r.log.Debug("downloading repository", slog.String("plugin", k), slog.String("ref", v.Ref)) + r.log.Debug("downloading repository", zap.String("plugin", k), zap.String("ref", v.Ref)) commits, rsp, err := r.client.Commits.ListCommits(v.Repo, &gitlab.ListCommitsOptions{ ListOptions: gitlab.ListOptions{ Page: 1, @@ -123,7 +123,7 @@ func (r *GLRepo) GetPluginsModData() ([]*velox.ModulesInfo, error) { modInfo.PseudoVersion = velox.ParseModuleInfo(modInfo.ModuleName, *at, modInfo.Version) if v.Replace != "" { - r.log.Debug("found replace, applying", slog.String("plugin", k), slog.String("path", v.Replace)) + r.log.Debug("found replace, applying", zap.String("plugin", k), zap.String("path", v.Replace)) } modInfo.Replace = v.Replace diff --git a/go.mod b/go.mod index 1a2b760..27a8845 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/roadrunner-server/velox/v2024 -go 1.23 - -toolchain go1.23.2 +go 1.23.2 require ( github.com/fatih/color v1.17.0 @@ -13,6 +11,7 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 github.com/xanzy/go-gitlab v0.112.0 + go.uber.org/zap v1.27.0 golang.org/x/mod v0.21.0 golang.org/x/oauth2 v0.23.0 ) diff --git a/go.sum b/go.sum index 2dcbc43..bbcbc3e 100644 --- a/go.sum +++ b/go.sum @@ -69,12 +69,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/xanzy/go-gitlab v0.111.0 h1:4zT52QdDVxGYAGxN2VY8upSvZIiuiI+Z4d+c+7D/lII= -github.com/xanzy/go-gitlab v0.111.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw= github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= diff --git a/internal/cli/build/build.go b/internal/cli/build/build.go index 9de0847..826ebff 100644 --- a/internal/cli/build/build.go +++ b/internal/cli/build/build.go @@ -1,7 +1,6 @@ package build import ( - "log/slog" "os" "syscall" @@ -10,13 +9,14 @@ import ( "github.com/roadrunner-server/velox/v2024/github" "github.com/roadrunner-server/velox/v2024/gitlab" "github.com/spf13/cobra" + "go.uber.org/zap" ) const ( ref string = "ref" ) -func BindCommand(cfg *velox.Config, out *string, zlog *slog.Logger) *cobra.Command { +func BindCommand(cfg *velox.Config, out *string, zlog *zap.Logger) *cobra.Command { return &cobra.Command{ Use: "build", Short: "Build RR", @@ -31,7 +31,7 @@ func BindCommand(cfg *velox.Config, out *string, zlog *slog.Logger) *cobra.Comma var mi []*velox.ModulesInfo if cfg.GitLab != nil { - rp, err := gitlab.NewGLRepoInfo(cfg, zlog.WithGroup("GITLAB")) + rp, err := gitlab.NewGLRepoInfo(cfg, zlog.Named("GITLAB")) if err != nil { return err } @@ -43,16 +43,16 @@ func BindCommand(cfg *velox.Config, out *string, zlog *slog.Logger) *cobra.Comma } // roadrunner located on the github - rp := github.NewGHRepoInfo(cfg, zlog.WithGroup("GITHUB")) + rp := github.NewGHRepoInfo(cfg, zlog.Named("GITHUB")) path, err := rp.DownloadTemplate(os.TempDir(), cfg.Roadrunner[ref]) if err != nil { - zlog.Error("downloading template", slog.Any("error", err)) + zlog.Error("downloading template", zap.Error(err)) os.Exit(1) } pMod, err := rp.GetPluginsModData() if err != nil { - zlog.Error("get plugins mod data", slog.Any("error", err)) + zlog.Error("get plugins mod data", zap.Error(err)) os.Exit(1) } @@ -61,13 +61,13 @@ func BindCommand(cfg *velox.Config, out *string, zlog *slog.Logger) *cobra.Comma pMod = append(pMod, mi...) } - err = builder.NewBuilder(path, pMod, *out, cfg.Roadrunner[ref], cfg.Debug.Enabled, zlog.WithGroup("BUILDER")).Build(cfg.Roadrunner[ref]) + err = builder.NewBuilder(path, pMod, *out, cfg.Roadrunner[ref], cfg.Debug.Enabled, zlog.Named("Builder")).Build(cfg.Roadrunner[ref]) if err != nil { - zlog.Error("fatal", slog.Any("error", err)) + zlog.Error("fatal", zap.Error(err)) os.Exit(1) } - zlog.Info("========= build finished successfully =========", slog.Any("RoadRunner binary can be found at", *out)) + zlog.Info("========= build finished successfully =========", zap.String("RoadRunner binary can be found at", *out)) return nil }, } diff --git a/internal/cli/root.go b/internal/cli/root.go index 8b3f0e0..d29e3cc 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -2,7 +2,6 @@ package cli import ( "fmt" - "log/slog" "runtime" "github.com/pkg/errors" @@ -12,11 +11,12 @@ import ( "github.com/roadrunner-server/velox/v2024/logger" "github.com/spf13/cobra" "github.com/spf13/viper" + "go.uber.org/zap" ) func NewCommand(executableName string) *cobra.Command { var config = &velox.Config{} // velox configuration - var lg = slog.Default() + lg, _ := zap.NewDevelopment() var cfgPath = strPtr("") var ( @@ -61,7 +61,11 @@ func NewCommand(executableName string) *cobra.Command { // [log] // level = "debug" // mode = "development" - zlog := logger.BuildLogger(config.Log["level"], config.Log["mode"]) + zlog, err := logger.BuildLogger(config.Log["level"], config.Log["mode"]) + if err != nil { + return err + } + *lg = *zlog return nil diff --git a/logger/logger.go b/logger/logger.go index be0d190..2c8b13a 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,53 +1,145 @@ package logger import ( - "log/slog" - "os" "strings" + "time" + + "github.com/fatih/color" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) // Mode represents available logger modes +type Mode string + const ( - production string = "production" - development string = "development" + none Mode = "none" + off Mode = "off" + production Mode = "production" + development Mode = "development" + raw Mode = "raw" ) -// BuildLogger converts config into Zap configuration. -func BuildLogger(level, mode string) *slog.Logger { - switch mode { +func BuildLogger(level, mode string) (*zap.Logger, error) { + var zCfg zap.Config + switch Mode(strings.ToLower(mode)) { + case off, none: + return zap.NewNop(), nil case production: - lg := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ - AddSource: false, - Level: stringToSlogLevel(level), - }) - - return slog.New(lg) + zCfg = zap.Config{ + Level: zap.NewAtomicLevelAt(zap.InfoLevel), + Development: false, + Encoding: "json", + EncoderConfig: zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + NameKey: "logger", + CallerKey: zapcore.OmitKey, + FunctionKey: zapcore.OmitKey, + MessageKey: "msg", + StacktraceKey: zapcore.OmitKey, + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: utcEpochTimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } case development: - lg := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - AddSource: false, - Level: stringToSlogLevel(level), - }) - return slog.New(lg) + zCfg = zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + Development: true, + Encoding: "console", + EncoderConfig: zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + NameKey: "logger", + CallerKey: zapcore.OmitKey, + FunctionKey: zapcore.OmitKey, + MessageKey: "msg", + StacktraceKey: zapcore.OmitKey, + EncodeLevel: ColoredLevelEncoder, + EncodeName: ColoredNameEncoder, + EncodeTime: utcISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } + case raw: + zCfg = zap.Config{ + Level: zap.NewAtomicLevelAt(zap.InfoLevel), + Encoding: "console", + EncoderConfig: zapcore.EncoderConfig{ + MessageKey: "message", + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } default: - return slog.Default() + zCfg = zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + Encoding: "console", + EncoderConfig: zapcore.EncoderConfig{ + TimeKey: "T", + LevelKey: "L", + NameKey: "N", + CallerKey: zapcore.OmitKey, + FunctionKey: zapcore.OmitKey, + MessageKey: "M", + StacktraceKey: zapcore.OmitKey, + EncodeLevel: ColoredLevelEncoder, + EncodeName: ColoredNameEncoder, + EncodeTime: utcISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } + } + + if level != "" { + lvl := zap.NewAtomicLevel() + if err := lvl.UnmarshalText([]byte(level)); err == nil { + zCfg.Level = lvl + } } + + return zCfg.Build() } -func stringToSlogLevel(level string) slog.Level { - switch strings.ToLower(level) { - case "debug": - return slog.LevelDebug - case "info": - return slog.LevelInfo - case "warn": - return slog.LevelWarn - case "error": - return slog.LevelError - case "fatal": - return slog.LevelError - case "panic": - return slog.LevelError - default: - return slog.LevelDebug +// ColoredLevelEncoder colorizes log levels. +func ColoredLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + switch level { + case zapcore.DebugLevel: + enc.AppendString(color.HiWhiteString(level.CapitalString())) + case zapcore.InfoLevel: + enc.AppendString(color.HiCyanString(level.CapitalString())) + case zapcore.WarnLevel: + enc.AppendString(color.HiYellowString(level.CapitalString())) + case zapcore.ErrorLevel, zapcore.DPanicLevel: + enc.AppendString(color.HiRedString(level.CapitalString())) + case zapcore.PanicLevel, zapcore.FatalLevel, zapcore.InvalidLevel: + enc.AppendString(color.HiMagentaString(level.CapitalString())) } } + +// ColoredNameEncoder colorizes service names. +func ColoredNameEncoder(s string, enc zapcore.PrimitiveArrayEncoder) { + if len(s) < 12 { + s += strings.Repeat(" ", 12-len(s)) + } + + enc.AppendString(color.HiGreenString(s)) +} + +func utcEpochTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendInt64(t.UTC().UnixNano()) +} + +func utcISO8601TimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.UTC().Format("2006-01-02T15:04:05-0700")) +}