From bb7f15aeea635396f20fe939ec7d4c7f4be5c5f6 Mon Sep 17 00:00:00 2001 From: jiangwel Date: Wed, 3 Sep 2025 19:32:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=8E=E7=AB=AF=E9=9B=86=E6=88=90sen?= =?UTF-8?q?try?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/server/main.go | 14 +++++ backend/config/config.go | 5 ++ backend/go.mod | 2 + backend/go.sum | 4 ++ backend/internal/user/handler/v1/user.go | 2 + backend/pkg/sentry/sentry.go | 67 ++++++++++++++++++++++++ 6 files changed, 94 insertions(+) create mode 100644 backend/pkg/sentry/sentry.go diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 128a9063..2bbf933e 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "time" "github.com/google/wire" "github.com/labstack/echo/v4" @@ -12,6 +13,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/docs" "github.com/chaitin/MonkeyCode/backend/internal" "github.com/chaitin/MonkeyCode/backend/pkg" + "github.com/chaitin/MonkeyCode/backend/pkg/sentry" "github.com/chaitin/MonkeyCode/backend/pkg/service" "github.com/chaitin/MonkeyCode/backend/pkg/store" ) @@ -28,6 +30,18 @@ func main() { s.version.Print() s.logger.With("config", s.config).Debug("config") + // 初始化Sentry + if err := sentry.Init(s.config, s.logger); err != nil { + s.logger.Error("Failed to initialize Sentry", "error", err) + } else { + // 添加Sentry中间件 + s.web.Echo().Use(sentry.Middleware()) + sentry.CaptureMessage("It works!") + s.logger.Info("Sentry middleware added") + // 确保在程序退出时刷新Sentry缓冲区 + defer sentry.Flush(2 * time.Second) + } + if s.config.Debug { s.web.Swagger("MonkeyCode API", "/reference", string(docs.SwaggerJSON), web.WithBasicAuth("mc", "mc88")) } diff --git a/backend/config/config.go b/backend/config/config.go index 384ee838..77f2b6bb 100644 --- a/backend/config/config.go +++ b/backend/config/config.go @@ -85,6 +85,10 @@ type Config struct { Security struct { QueueLimit int `mapstructure:"queue_limit"` } `mapstructure:"security"` + + Sentry struct { + DSN string `mapstructure:"dsn"` + } `mapstructure:"sentry"` } func (c *Config) GetBaseURL(req *http.Request, settings *domain.Setting) string { @@ -154,6 +158,7 @@ func Init() (*Config, error) { v.SetDefault("embedding.model_name", "qwen3-embedding-0.6b") v.SetDefault("embedding.api_endpoint", "https://aiapi.chaitin.net/v1/embeddings") v.SetDefault("embedding.api_key", "") + v.SetDefault("sentry.dsn", "") c := Config{} if err := v.Unmarshal(&c); err != nil { diff --git a/backend/go.mod b/backend/go.mod index ae54381e..f8e615c4 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -63,6 +63,8 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/getkin/kin-openapi v0.118.0 // indirect + github.com/getsentry/sentry-go v0.35.1 // indirect + github.com/getsentry/sentry-go/echo v0.35.1 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/inflect v0.21.2 // indirect diff --git a/backend/go.sum b/backend/go.sum index a787ef9d..2d9499ed 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -109,6 +109,10 @@ github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFA github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ= +github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= +github.com/getsentry/sentry-go/echo v0.35.1 h1:MIhSUyo7cpCdcw0/lIeAw5fukrDt3x9G7qbiyjbVllI= +github.com/getsentry/sentry-go/echo v0.35.1/go.mod h1:IjdEzgvwlP2/7A32tWk75UmSUsBqvKFdpkN6WhB1e6M= github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= diff --git a/backend/internal/user/handler/v1/user.go b/backend/internal/user/handler/v1/user.go index f80310eb..c63edaa9 100644 --- a/backend/internal/user/handler/v1/user.go +++ b/backend/internal/user/handler/v1/user.go @@ -19,6 +19,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/domain" "github.com/chaitin/MonkeyCode/backend/errcode" "github.com/chaitin/MonkeyCode/backend/internal/middleware" + "github.com/chaitin/MonkeyCode/backend/pkg/sentry" "github.com/chaitin/MonkeyCode/backend/pkg/session" "github.com/chaitin/MonkeyCode/backend/pkg/version" "github.com/chaitin/MonkeyCode/backend/pkg/vsix" @@ -715,6 +716,7 @@ func (h *UserHandler) InitAdmin() error { func (h *UserHandler) ExportCompletionData(c *web.Context) error { data, err := h.usecase.ExportCompletionData(c.Request().Context()) if err != nil { + sentry.CaptureError(err) return err } diff --git a/backend/pkg/sentry/sentry.go b/backend/pkg/sentry/sentry.go new file mode 100644 index 00000000..635eafe1 --- /dev/null +++ b/backend/pkg/sentry/sentry.go @@ -0,0 +1,67 @@ +package sentry + +import ( + "log/slog" + "time" + + sentrygo "github.com/getsentry/sentry-go" + sentryecho "github.com/getsentry/sentry-go/echo" + "github.com/labstack/echo/v4" + + "github.com/chaitin/MonkeyCode/backend/config" +) + +// Init 初始化Sentry +func Init(cfg *config.Config, logger *slog.Logger) error { + if cfg.Sentry.DSN == "" { + logger.Info("Sentry DSN not configured, skipping Sentry initialization") + return nil + } + + err := sentrygo.Init(sentrygo.ClientOptions{ + Dsn: cfg.Sentry.DSN, + }) + + if err != nil { + return err + } + + logger.Info("Sentry initialized successfully") + + return nil +} + +// Middleware 返回Echo的Sentry中间件 +func Middleware() echo.MiddlewareFunc { + return sentryecho.New(sentryecho.Options{ + Repanic: true, + Timeout: 2 * time.Second, + }) +} + +// CaptureError 捕获错误并发送到Sentry +func CaptureError(err error) { + if err != nil { + sentrygo.CaptureException(err) + } +} + +// CaptureMessage 捕获消息并发送到Sentry +func CaptureMessage(message string) { + sentrygo.CaptureMessage(message) +} + +// AddBreadcrumb 添加面包屑 +func AddBreadcrumb(breadcrumb *sentrygo.Breadcrumb) { + sentrygo.AddBreadcrumb(breadcrumb) +} + +// ConfigureScope 配置作用域 +func ConfigureScope(f func(scope *sentrygo.Scope)) { + sentrygo.ConfigureScope(f) +} + +// Flush 刷新Sentry缓冲区 +func Flush(timeout time.Duration) bool { + return sentrygo.Flush(timeout) +} \ No newline at end of file