1
0
Fork 1
mirror of https://github.com/0x2E/fusion.git synced 2025-06-08 05:27:15 +09:00

refactor: replace zap log with slog (#150)

* refactor: replace zap log with slog

* fix
This commit is contained in:
Yuan 2025-04-25 17:18:25 +08:00 committed by GitHub
parent 5c98073f9f
commit bc8109fe39
Signed by: github
GPG key ID: B5690EEEBB952194
10 changed files with 55 additions and 79 deletions

View file

@ -3,6 +3,7 @@ package api
import (
"errors"
"fmt"
"log/slog"
"net/http"
"strings"
"time"
@ -10,7 +11,6 @@ import (
"github.com/0x2e/fusion/auth"
"github.com/0x2e/fusion/conf"
"github.com/0x2e/fusion/frontend"
"github.com/0x2e/fusion/pkg/logx"
"github.com/0x2e/fusion/repo"
"github.com/0x2e/fusion/server"
@ -35,7 +35,6 @@ type Params struct {
func Run(params Params) {
r := echo.New()
apiLogger := logx.Logger.With("module", "api")
if conf.Debug {
r.Debug = true
@ -43,7 +42,7 @@ func Run(params Params) {
if len(resBody) > 500 {
resBody = append(resBody[:500], []byte("...")...)
}
apiLogger.Debugw("body dump", "req", reqBody, "resp", resBody)
slog.Debug("body dump", "req", reqBody, "resp", resBody)
}))
}
@ -61,9 +60,9 @@ func Run(params Params) {
return nil
}
if v.Error == nil {
apiLogger.Infow("REQUEST", "uri", v.URI, "status", v.Status)
slog.Info("REQUEST", "uri", v.URI, "status", v.Status)
} else {
apiLogger.Errorw(v.Error.Error(), "uri", v.URI, "status", v.Status)
slog.Error(v.Error.Error(), "uri", v.URI, "status", v.Status)
}
return nil
},
@ -144,7 +143,8 @@ func Run(params Params) {
err = r.Start(addr)
}
if err != nil {
apiLogger.Fatalln(err)
slog.Error(err.Error())
return
}
}

View file

@ -1,29 +1,42 @@
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"os"
"log/slog"
"github.com/0x2e/fusion/api"
"github.com/0x2e/fusion/conf"
"github.com/0x2e/fusion/pkg/logx"
"github.com/0x2e/fusion/repo"
"github.com/0x2e/fusion/service/pull"
)
func main() {
defer logx.Logger.Sync()
l := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
slog.SetDefault(l)
if conf.Debug {
l := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
slog.SetDefault(l)
go func() {
logx.Logger.Infoln(http.ListenAndServe("localhost:6060", nil))
if err := http.ListenAndServe("localhost:6060", nil); err != nil {
slog.Error("pprof server", "error", err)
return
}
}()
}
config, err := conf.Load()
if err != nil {
log.Fatalf("failed to load configuration: %v", err)
slog.Error("failed to load configuration", "error", err)
return
}
repo.Init(config.DB)

View file

@ -3,7 +3,7 @@ package conf
import (
"errors"
"fmt"
"log"
"log/slog"
"os"
"github.com/0x2e/fusion/auth"
@ -32,9 +32,9 @@ func Load() (Conf, error) {
if !os.IsNotExist(err) {
return Conf{}, err
}
log.Printf("no configuration file found at %s", dotEnvFilename)
slog.Warn(fmt.Sprintf("no configuration file found at %s", dotEnvFilename))
} else {
log.Printf("read configuration from %s", dotEnvFilename)
slog.Info(fmt.Sprintf("load configuration from %s", dotEnvFilename))
}
var conf struct {
Host string `env:"HOST" envDefault:"0.0.0.0"`
@ -46,11 +46,9 @@ func Load() (Conf, error) {
TLSKey string `env:"TLS_KEY"`
}
if err := env.Parse(&conf); err != nil {
panic(err)
}
if Debug {
fmt.Println(conf)
return Conf{}, err
}
slog.Debug("configuration loaded", "conf", conf)
var pwHash *auth.HashedPassword
if conf.Password != "" {

2
go.mod
View file

@ -15,7 +15,6 @@ require (
github.com/labstack/echo/v4 v4.13.3
github.com/mmcdole/gofeed v1.3.0
github.com/stretchr/testify v1.10.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.37.0
gorm.io/gorm v1.25.12
gorm.io/plugin/soft_delete v1.2.1
@ -47,7 +46,6 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect

6
go.sum
View file

@ -99,12 +99,6 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=

View file

@ -1,39 +0,0 @@
package logx
import (
"context"
"github.com/0x2e/fusion/conf"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var Logger = initLogger()
func initLogger() *zap.SugaredLogger {
var logger *zap.Logger
if conf.Debug {
devConf := zap.NewDevelopmentConfig()
devConf.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
logger = zap.Must(devConf.Build())
} else {
prodConf := zap.NewProductionConfig()
prodConf.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger = zap.Must(prodConf.Build())
}
return logger.Sugar()
}
type Logx struct{}
func ContextWithLogger(ctx context.Context, l *zap.SugaredLogger) context.Context {
return context.WithValue(ctx, Logx{}, l)
}
func LoggerFromContext(ctx context.Context) *zap.SugaredLogger {
if l, ok := ctx.Value(Logx{}).(*zap.SugaredLogger); ok {
return l
}
return Logger
}

View file

@ -4,3 +4,13 @@ package ptr
func To[T any](v T) *T {
return &v
}
// From returns the value pointed to by the given pointer.
// If the pointer is nil, it returns the zero value of the type.
func From[T any](v *T) T {
if v == nil {
var zero T
return zero
}
return *v
}

View file

@ -2,26 +2,29 @@ package pull
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/0x2e/fusion/model"
"github.com/0x2e/fusion/pkg/ptr"
"github.com/0x2e/fusion/service/pull/client"
)
func (p *Puller) do(ctx context.Context, f *model.Feed, force bool) error {
logger := pullLogger.With("feed_id", f.ID, "feed_name", f.Name)
logger := slog.With("feed_id", f.ID, "feed_link", ptr.From(f.Link))
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
updateAction, skipReason := DecideFeedUpdateAction(f, time.Now())
if skipReason == &SkipReasonSuspended {
logger.Infof("skip: %s", skipReason)
logger.Info(fmt.Sprintf("skip: %s", skipReason))
return nil
}
if !force {
switch updateAction {
case ActionSkipUpdate:
logger.Infof("skip: %s", skipReason)
logger.Info(fmt.Sprintf("skip: %s", skipReason))
return nil
case ActionFetchUpdate:
// Proceed to perform the fetch.
@ -69,8 +72,7 @@ func DecideFeedUpdateAction(f *model.Feed, now time.Time) (FeedUpdateAction, *Fe
backoffTime := CalculateBackoffTime(f.ConsecutiveFailures)
timeSinceUpdate := now.Sub(f.UpdatedAt)
if timeSinceUpdate < backoffTime {
logger := pullLogger.With("feed_id", f.ID, "feed_name", f.Name)
logger.Infof("%d consecutive feed update failures, so next attempt is after %v", f.ConsecutiveFailures, f.UpdatedAt.Add(backoffTime).Format(time.RFC3339))
slog.Info(fmt.Sprintf("%d consecutive feed update failures, so next attempt is after %v", f.ConsecutiveFailures, f.UpdatedAt.Add(backoffTime).Format(time.RFC3339)), "feed_id", f.ID, "feed_link", ptr.From(f.Link))
return ActionSkipUpdate, &SkipReasonCoolingOff
}
} else if now.Sub(f.UpdatedAt) < interval {

View file

@ -3,17 +3,17 @@ package pull
import (
"context"
"errors"
"log/slog"
"sync"
"time"
"github.com/0x2e/fusion/model"
"github.com/0x2e/fusion/pkg/logx"
"github.com/0x2e/fusion/pkg/ptr"
"github.com/0x2e/fusion/repo"
)
var (
interval = 30 * time.Minute
pullLogger = logx.Logger.With("module", "puller")
interval = 30 * time.Minute
)
type FeedRepo interface {
@ -52,7 +52,6 @@ func (p *Puller) Run() {
}
func (p *Puller) PullAll(ctx context.Context, force bool) error {
logger := logx.LoggerFromContext(ctx)
ctx, cancel := context.WithTimeout(ctx, interval/2)
defer cancel()
@ -80,8 +79,7 @@ func (p *Puller) PullAll(ctx context.Context, force bool) error {
}()
if err := p.do(ctx, f, force); err != nil {
logger := logger.With("feed_id", f.ID)
logger.Errorln(err)
slog.Error("failed to pull feed", "error", err, "feed_id", f.ID, "feed_link", ptr.From(f.Link))
}
}(f)
}

View file

@ -2,6 +2,8 @@ package pull
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/0x2e/fusion/model"
@ -76,14 +78,14 @@ func (r *defaultSingleFeedRepo) RecordFailure(readErr error) error {
}
func (p SingleFeedPuller) Pull(ctx context.Context, feed *model.Feed) error {
logger := pullLogger.With("feed_id", feed.ID, "feed_name", feed.Name)
logger := slog.With("feed_id", feed.ID, "feed_link", ptr.From(feed.Link))
// We don't exit on error, as we want to record any error in the data store.
fetchResult, readErr := p.readFeed(ctx, *feed.Link, feed.FeedRequestOptions)
if readErr == nil {
logger.Infof("fetched %d items", len(fetchResult.Items))
logger.Info(fmt.Sprintf("fetched %d items", len(fetchResult.Items)))
} else {
logger.Infof("fetch failed: %v", readErr)
logger.Warn("failed to fetch feed", "error", readErr)
}
return p.updateFeedInStore(feed.ID, fetchResult.Items, fetchResult.LastBuild, readErr)