From dfa78cac49a8cac6d83cfd58b2f645e810dcb924 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 29 Feb 2024 23:54:43 +0800 Subject: [PATCH] chore: update logger --- bin/memos/main.go | 17 +++--- internal/jobs/presign_link.go | 10 ++-- internal/log/logger.go | 66 ----------------------- plugin/telegram/attachment.go | 9 +--- plugin/telegram/bot.go | 15 ++---- server/route/api/v1/jwt.go | 7 ++- server/route/api/v1/memo.go | 11 ++-- server/route/api/v1/resource.go | 3 -- server/route/api/v2/logger_interceptor.go | 44 +++++++++++++++ server/route/api/v2/memo_service.go | 9 ++-- server/route/api/v2/v2.go | 6 +-- server/route/resource/resource.go | 5 +- server/server.go | 25 +++------ store/db/mysql/mysql.go | 6 +-- 14 files changed, 87 insertions(+), 146 deletions(-) delete mode 100644 internal/log/logger.go create mode 100644 server/route/api/v2/logger_interceptor.go diff --git a/bin/memos/main.go b/bin/memos/main.go index 5110e323..9669d818 100644 --- a/bin/memos/main.go +++ b/bin/memos/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "log/slog" "net/http" "os" "os/signal" @@ -10,10 +11,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "go.uber.org/zap" "github.com/usememos/memos/internal/jobs" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/server" _profile "github.com/usememos/memos/server/profile" "github.com/usememos/memos/store" @@ -49,26 +48,26 @@ var ( dbDriver, err := db.NewDBDriver(profile) if err != nil { cancel() - log.Error("failed to create db driver", zap.Error(err)) + slog.Error("failed to create db driver", err) return } if err := dbDriver.Migrate(ctx); err != nil { cancel() - log.Error("failed to migrate db", zap.Error(err)) + slog.Error("failed to migrate database", err) return } storeInstance := store.New(dbDriver, profile) if err := storeInstance.MigrateManually(ctx); err != nil { cancel() - log.Error("failed to migrate manually", zap.Error(err)) + slog.Error("failed to migrate manually", err) return } s, err := server.NewServer(ctx, profile, storeInstance) if err != nil { cancel() - log.Error("failed to create server", zap.Error(err)) + slog.Error("failed to create server", err) return } @@ -78,8 +77,7 @@ var ( // which is taken as the graceful shutdown signal for many systems, eg., Kubernetes, Gunicorn. signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { - sig := <-c - log.Info(fmt.Sprintf("%s received.\n", sig.String())) + <-c s.Shutdown(ctx) cancel() }() @@ -91,7 +89,7 @@ var ( if err := s.Start(ctx); err != nil { if err != http.ErrServerClosed { - log.Error("failed to start server", zap.Error(err)) + slog.Error("failed to start server", err) cancel() } } @@ -103,7 +101,6 @@ var ( ) func Execute() error { - defer log.Sync() return rootCmd.Execute() } diff --git a/internal/jobs/presign_link.go b/internal/jobs/presign_link.go index 9f6e90df..18761006 100644 --- a/internal/jobs/presign_link.go +++ b/internal/jobs/presign_link.go @@ -3,13 +3,12 @@ package jobs import ( "context" "encoding/json" + "log/slog" "strings" "time" "github.com/pkg/errors" - "go.uber.org/zap" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/plugin/storage/s3" apiv1 "github.com/usememos/memos/server/route/api/v1" "github.com/usememos/memos/store" @@ -19,11 +18,10 @@ import ( // It uses S3 client to generate presigned URLs and updates the corresponding resources in the store. func RunPreSignLinks(ctx context.Context, dataStore *store.Store) { for { - started := time.Now() if err := signExternalLinks(ctx, dataStore); err != nil { - log.Warn("failed sign external links", zap.Error(err)) + slog.Error("failed to pre-sign links", err) } else { - log.Info("links pre-signed", zap.Duration("duration", time.Since(started))) + slog.Debug("pre-signed links") } select { case <-time.After(s3.LinkLifetime / 2): @@ -69,7 +67,7 @@ func signExternalLinks(ctx context.Context, dataStore *store.Store) error { } newLink, err := objectStore.PreSignLink(ctx, res.ExternalLink) if err != nil { - log.Warn("failed pre-sign link", zap.Int32("resource", res.ID), zap.String("link", res.ExternalLink), zap.Error(err)) + slog.Error("failed to pre-sign link", err) continue // do not fail - we may want update left over links too } now := time.Now().Unix() diff --git a/internal/log/logger.go b/internal/log/logger.go deleted file mode 100644 index c8395214..00000000 --- a/internal/log/logger.go +++ /dev/null @@ -1,66 +0,0 @@ -package log - -import ( - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -var ( - // `gl` is the global logger. - // Other packages should use public methods such as Info/Error to do the logging. - // For other types of logging, e.g. logging to a separate file, they should use their own loggers. - gl *zap.Logger - gLevel zap.AtomicLevel -) - -// Initializes the global console logger. -func init() { - gLevel = zap.NewAtomicLevelAt(zap.InfoLevel) - gl, _ = zap.Config{ - Level: gLevel, - Development: true, - // Use "console" to print readable stacktrace. - Encoding: "console", - EncoderConfig: zap.NewDevelopmentEncoderConfig(), - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - }.Build( - // Skip one caller stack to locate the correct caller. - zap.AddCallerSkip(1), - ) -} - -// SetLevel wraps the zap Level's SetLevel method. -func SetLevel(level zapcore.Level) { - gLevel.SetLevel(level) -} - -// EnabledLevel wraps the zap Level's Enabled method. -func EnabledLevel(level zapcore.Level) bool { - return gLevel.Enabled(level) -} - -// Debug wraps the zap Logger's Debug method. -func Debug(msg string, fields ...zap.Field) { - gl.Debug(msg, fields...) -} - -// Info wraps the zap Logger's Info method. -func Info(msg string, fields ...zap.Field) { - gl.Info(msg, fields...) -} - -// Warn wraps the zap Logger's Warn method. -func Warn(msg string, fields ...zap.Field) { - gl.Warn(msg, fields...) -} - -// Error wraps the zap Logger's Error method. -func Error(msg string, fields ...zap.Field) { - gl.Error(msg, fields...) -} - -// Sync wraps the zap Logger's Sync method. -func Sync() { - _ = gl.Sync() -} diff --git a/plugin/telegram/attachment.go b/plugin/telegram/attachment.go index b7ed7cb1..4917307a 100644 --- a/plugin/telegram/attachment.go +++ b/plugin/telegram/attachment.go @@ -1,11 +1,8 @@ package telegram import ( + "log/slog" "path/filepath" - - "go.uber.org/zap" - - "github.com/usememos/memos/internal/log" ) type Attachment struct { @@ -29,9 +26,7 @@ func (b Attachment) GetMimeType() string { mime, ok := mimeTypes[filepath.Ext(b.FileName)] if !ok { - // Handle unknown file extension - log.Warn("Unknown file type for ", zap.String("filename", b.FileName)) - + slog.Warn("Unknown file extension", slog.String("file", b.FileName)) return "application/octet-stream" } diff --git a/plugin/telegram/bot.go b/plugin/telegram/bot.go index aa08f961..de8bc8e6 100644 --- a/plugin/telegram/bot.go +++ b/plugin/telegram/bot.go @@ -3,13 +3,9 @@ package telegram import ( "context" "errors" - "fmt" + "log/slog" "strings" "time" - - "go.uber.org/zap" - - "github.com/usememos/memos/internal/log" ) type Handler interface { @@ -41,7 +37,6 @@ func (b *Bot) Start(ctx context.Context) { continue } if err != nil { - log.Warn("fail to telegram.GetUpdates", zap.Error(err)) time.Sleep(errRetryWait) continue } @@ -56,7 +51,7 @@ func (b *Bot) Start(ctx context.Context) { if update.CallbackQuery != nil { err := b.handler.CallbackQueryHandle(ctx, b, *update.CallbackQuery) if err != nil { - log.Error("fail to handle CallbackQuery", zap.Error(err)) + slog.Error("fail to handle callback query", err) } continue @@ -70,7 +65,7 @@ func (b *Bot) Start(ctx context.Context) { if !message.IsSupported() { _, err := b.SendReplyMessage(ctx, message.Chat.ID, message.MessageID, "Supported messages: animation, audio, text, document, photo, video, video note, voice, other messages with caption") if err != nil { - log.Error(fmt.Sprintf("fail to telegram.SendReplyMessage for messageID=%d", message.MessageID), zap.Error(err)) + slog.Error("fail to send reply message", err) } continue } @@ -88,12 +83,12 @@ func (b *Bot) Start(ctx context.Context) { err = b.handleSingleMessages(ctx, singleMessages) if err != nil { - log.Error("fail to handle singleMessage", zap.Error(err)) + slog.Error("fail to handle plain text message", err) } err = b.handleGroupMessages(ctx, groupMessages) if err != nil { - log.Error("fail to handle plain text message", zap.Error(err)) + slog.Error("fail to handle media group message", err) } } } diff --git a/server/route/api/v1/jwt.go b/server/route/api/v1/jwt.go index a2507221..3a60eedb 100644 --- a/server/route/api/v1/jwt.go +++ b/server/route/api/v1/jwt.go @@ -2,15 +2,14 @@ package v1 import ( "fmt" + "log/slog" "net/http" "strings" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo/v4" "github.com/pkg/errors" - "go.uber.org/zap" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/internal/util" storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/server/route/api/auth" @@ -83,7 +82,7 @@ func JWTMiddleware(server *APIV1Service, next echo.HandlerFunc, secret string) e if err != nil { err = removeAccessTokenAndCookies(c, server.Store, userID, accessToken) if err != nil { - log.Error("fail to remove AccessToken and Cookies", zap.Error(err)) + slog.Warn("fail to remove AccessToken and Cookies", err) } return echo.NewHTTPError(http.StatusUnauthorized, "Invalid or expired access token") } @@ -95,7 +94,7 @@ func JWTMiddleware(server *APIV1Service, next echo.HandlerFunc, secret string) e if !validateAccessToken(accessToken, accessTokens) { err = removeAccessTokenAndCookies(c, server.Store, userID, accessToken) if err != nil { - log.Error("fail to remove AccessToken and Cookies", zap.Error(err)) + slog.Warn("fail to remove AccessToken and Cookies", err) } return echo.NewHTTPError(http.StatusUnauthorized, "Invalid access token.") } diff --git a/server/route/api/v1/memo.go b/server/route/api/v1/memo.go index 13ce19ad..90f01f08 100644 --- a/server/route/api/v1/memo.go +++ b/server/route/api/v1/memo.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "strconv" "time" @@ -11,9 +12,7 @@ import ( "github.com/labstack/echo/v4" "github.com/lithammer/shortuuid/v4" "github.com/pkg/errors" - "go.uber.org/zap" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/internal/util" "github.com/usememos/memos/plugin/webhook" storepb "github.com/usememos/memos/proto/gen/store" @@ -390,7 +389,6 @@ func (s *APIV1Service) CreateMemo(c echo.Context) error { for _, userSetting := range userSettings { tgUserID, err := strconv.ParseInt(userSetting.GetTelegramUserId(), 10, 64) if err != nil { - log.Error("failed to parse Telegram UserID", zap.Error(err)) continue } @@ -398,14 +396,13 @@ func (s *APIV1Service) CreateMemo(c echo.Context) error { content := memoResponse.CreatorName + " Says:\n\n" + memoResponse.Content _, err = s.telegramBot.SendMessage(ctx, tgUserID, content) if err != nil { - log.Error("Failed to send Telegram notification", zap.Error(err)) continue } } } // Try to dispatch webhook when memo is created. if err := s.DispatchMemoCreatedWebhook(ctx, memoResponse); err != nil { - log.Warn("Failed to dispatch memo created webhook", zap.Error(err)) + slog.Warn("Failed to dispatch memo created webhook", err) } return c.JSON(http.StatusOK, memoResponse) @@ -627,7 +624,7 @@ func (s *APIV1Service) DeleteMemo(c echo.Context) error { if memoMessage, err := s.convertMemoFromStore(ctx, memo); err == nil { // Try to dispatch webhook when memo is deleted. if err := s.DispatchMemoDeletedWebhook(ctx, memoMessage); err != nil { - log.Warn("Failed to dispatch memo deleted webhook", zap.Error(err)) + slog.Warn("Failed to dispatch memo deleted webhook", err) } } @@ -821,7 +818,7 @@ func (s *APIV1Service) UpdateMemo(c echo.Context) error { } // Try to dispatch webhook when memo is updated. if err := s.DispatchMemoUpdatedWebhook(ctx, memoResponse); err != nil { - log.Warn("Failed to dispatch memo updated webhook", zap.Error(err)) + slog.Error("Failed to dispatch memo updated webhook", err) } return c.JSON(http.StatusOK, memoResponse) diff --git a/server/route/api/v1/resource.go b/server/route/api/v1/resource.go index ab7c6f68..6f511695 100644 --- a/server/route/api/v1/resource.go +++ b/server/route/api/v1/resource.go @@ -17,9 +17,7 @@ import ( "github.com/labstack/echo/v4" "github.com/lithammer/shortuuid/v4" "github.com/pkg/errors" - "go.uber.org/zap" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/internal/util" "github.com/usememos/memos/plugin/storage/s3" "github.com/usememos/memos/store" @@ -192,7 +190,6 @@ func (s *APIV1Service) UploadResource(c echo.Context) error { if settingMaxUploadSizeMiB, err := strconv.Atoi(maxUploadSetting.Value); err == nil { settingMaxUploadSizeBytes = settingMaxUploadSizeMiB * MebiByte } else { - log.Warn("Failed to parse max upload size", zap.Error(err)) settingMaxUploadSizeBytes = 0 } } else { diff --git a/server/route/api/v2/logger_interceptor.go b/server/route/api/v2/logger_interceptor.go new file mode 100644 index 00000000..7332d255 --- /dev/null +++ b/server/route/api/v2/logger_interceptor.go @@ -0,0 +1,44 @@ +package v2 + +import ( + "context" + "log/slog" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type LoggerInterceptor struct { +} + +func NewLoggerInterceptor() *LoggerInterceptor { + return &LoggerInterceptor{} +} + +func (in *LoggerInterceptor) LoggerInterceptor(ctx context.Context, request any, serverInfo *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + resp, err := handler(ctx, request) + in.loggerInterceptorDo(ctx, serverInfo.FullMethod, err) + return resp, err +} + +func (*LoggerInterceptor) loggerInterceptorDo(ctx context.Context, fullMethod string, err error) { + st := status.Convert(err) + var logLevel slog.Level + var logMsg string + switch st.Code() { + case codes.OK: + logLevel = slog.LevelInfo + logMsg = "OK" + case codes.Unauthenticated, codes.OutOfRange, codes.PermissionDenied, codes.NotFound: + logLevel = slog.LevelInfo + logMsg = "client error" + case codes.Internal, codes.Unknown, codes.DataLoss, codes.Unavailable, codes.DeadlineExceeded: + logLevel = slog.LevelError + logMsg = "server error" + default: + logLevel = slog.LevelError + logMsg = "unknown error" + } + slog.LogAttrs(ctx, logLevel, logMsg, slog.String("method", fullMethod)) +} diff --git a/server/route/api/v2/memo_service.go b/server/route/api/v2/memo_service.go index 7add198f..8eb732fc 100644 --- a/server/route/api/v2/memo_service.go +++ b/server/route/api/v2/memo_service.go @@ -6,18 +6,17 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "time" "github.com/google/cel-go/cel" "github.com/lithammer/shortuuid/v4" "github.com/pkg/errors" - "go.uber.org/zap" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/internal/util" "github.com/usememos/memos/plugin/webhook" apiv2pb "github.com/usememos/memos/proto/gen/api/v2" @@ -70,7 +69,7 @@ func (s *APIV2Service) CreateMemo(ctx context.Context, request *apiv2pb.CreateMe } // Try to dispatch webhook when memo is created. if err := s.DispatchMemoCreatedWebhook(ctx, memoMessage); err != nil { - log.Warn("Failed to dispatch memo created webhook", zap.Error(err)) + slog.Warn("Failed to dispatch memo created webhook", err) } response := &apiv2pb.CreateMemoResponse{ @@ -280,7 +279,7 @@ func (s *APIV2Service) UpdateMemo(ctx context.Context, request *apiv2pb.UpdateMe } // Try to dispatch webhook when memo is updated. if err := s.DispatchMemoUpdatedWebhook(ctx, memoMessage); err != nil { - log.Warn("Failed to dispatch memo updated webhook", zap.Error(err)) + slog.Warn("Failed to dispatch memo updated webhook", err) } return &apiv2pb.UpdateMemoResponse{ @@ -307,7 +306,7 @@ func (s *APIV2Service) DeleteMemo(ctx context.Context, request *apiv2pb.DeleteMe if memoMessage, err := s.convertMemoFromStore(ctx, memo); err == nil { // Try to dispatch webhook when memo is deleted. if err := s.DispatchMemoDeletedWebhook(ctx, memoMessage); err != nil { - log.Warn("Failed to dispatch memo deleted webhook", zap.Error(err)) + slog.Warn("Failed to dispatch memo deleted webhook", err) } } diff --git a/server/route/api/v2/v2.go b/server/route/api/v2/v2.go index 176e94a5..e416a3ee 100644 --- a/server/route/api/v2/v2.go +++ b/server/route/api/v2/v2.go @@ -3,18 +3,17 @@ package v2 import ( "context" "fmt" + "log/slog" "net" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/improbable-eng/grpc-web/go/grpcweb" "github.com/labstack/echo/v4" "github.com/pkg/errors" - "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/reflection" - "github.com/usememos/memos/internal/log" apiv2pb "github.com/usememos/memos/proto/gen/api/v2" "github.com/usememos/memos/server/profile" "github.com/usememos/memos/store" @@ -45,6 +44,7 @@ func NewAPIV2Service(secret string, profile *profile.Profile, store *store.Store authProvider := NewGRPCAuthInterceptor(store, secret) grpcServer := grpc.NewServer( grpc.ChainUnaryInterceptor( + NewLoggerInterceptor().LoggerInterceptor, authProvider.AuthenticationInterceptor, ), ) @@ -138,7 +138,7 @@ func (s *APIV2Service) RegisterGateway(ctx context.Context, e *echo.Echo) error } go func() { if err := s.grpcServer.Serve(listen); err != nil { - log.Error("grpc server listen error", zap.Error(err)) + slog.Error("failed to start gRPC server", err) } }() diff --git a/server/route/resource/resource.go b/server/route/resource/resource.go index 622a0344..7e977f90 100644 --- a/server/route/resource/resource.go +++ b/server/route/resource/resource.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "log/slog" "net/http" "os" "path/filepath" @@ -14,9 +15,7 @@ import ( "github.com/disintegration/imaging" "github.com/labstack/echo/v4" "github.com/pkg/errors" - "go.uber.org/zap" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/internal/util" "github.com/usememos/memos/server/profile" "github.com/usememos/memos/store" @@ -99,7 +98,7 @@ func (s *ResourceService) streamResource(c echo.Context) error { thumbnailPath := filepath.Join(s.Profile.Data, thumbnailImagePath, fmt.Sprintf("%d%s", resource.ID, ext)) thumbnailBlob, err := getOrGenerateThumbnailImage(blob, thumbnailPath) if err != nil { - log.Warn(fmt.Sprintf("failed to get or generate local thumbnail with path %s", thumbnailPath), zap.Error(err)) + slog.Warn("failed to get or generate thumbnail image", err) } else { blob = thumbnailBlob } diff --git a/server/server.go b/server/server.go index 98237fdc..9d9fc8d0 100644 --- a/server/server.go +++ b/server/server.go @@ -9,7 +9,6 @@ import ( "github.com/google/uuid" "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" "github.com/pkg/errors" "github.com/usememos/memos/plugin/telegram" @@ -49,31 +48,15 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store telegramBot: telegram.NewBotWithHandler(integration.NewTelegramHandler(store)), } - e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ - Format: `{"time":"${time_rfc3339}","latency":"${latency_human}",` + - `"method":"${method}","uri":"${uri}",` + - `"status":${status},"error":"${error}"}` + "\n", - })) - + // Register CORS middleware. e.Use(CORSMiddleware()) - e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{ - Skipper: grpcRequestSkipper, - Timeout: 30 * time.Second, - })) - serverID, err := s.getSystemServerID(ctx) if err != nil { return nil, errors.Wrap(err, "failed to retrieve system server ID") } s.ID = serverID - // Only serve frontend when it's enabled. - if profile.Frontend { - frontendService := frontend.NewFrontendService(profile, store) - frontendService.Serve(ctx, e) - } - secret := "usememos" if profile.Mode == "prod" { secret, err = s.getSystemSecretSessionName(ctx) @@ -88,6 +71,12 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store return c.String(http.StatusOK, "Service ready.") }) + // Only serve frontend when it's enabled. + if profile.Frontend { + frontendService := frontend.NewFrontendService(profile, store) + frontendService.Serve(ctx, e) + } + // Register API v1 endpoints. rootGroup := e.Group("") apiV1Service := apiv1.NewAPIV1Service(s.Secret, profile, store, s.telegramBot) diff --git a/store/db/mysql/mysql.go b/store/db/mysql/mysql.go index b16c64bb..3dee829b 100644 --- a/store/db/mysql/mysql.go +++ b/store/db/mysql/mysql.go @@ -3,12 +3,11 @@ package mysql import ( "context" "database/sql" - "fmt" + "log/slog" "github.com/go-sql-driver/mysql" "github.com/pkg/errors" - "github.com/usememos/memos/internal/log" "github.com/usememos/memos/server/profile" "github.com/usememos/memos/store" ) @@ -31,7 +30,6 @@ func NewDB(profile *profile.Profile) (store.Driver, error) { driver := DB{profile: profile} driver.config, err = mysql.ParseDSN(dsn) if err != nil { - log.Error(fmt.Sprintf("DSN parse error: %s", dsn)) return nil, errors.New("Parse DSN eroor") } @@ -87,7 +85,7 @@ func (d *DB) GetCurrentDBSize(ctx context.Context) (int64, error) { " GROUP BY `table_schema`" rows, err := d.db.QueryContext(ctx, query, d.config.DBName) if err != nil { - log.Error("Query db size error, make sure you have enough privilege") + slog.Error("Query db size error, make sure you have enough privilege", err) return 0, err } defer rows.Close()