diff --git a/CHANGELOG.md b/CHANGELOG.md index 16d0269e..f7bf2c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to paopao-ce are documented in this file. - add custom comment sort strategy support [#243](https://github.com/rocboss/paopao-ce/pull/243) - add `RedisCacheIndex` feature [#250](https://github.com/rocboss/paopao-ce/pull/250) +- add `Sentry` feature [#258](https://github.com/rocboss/paopao-ce/pull/258) ### Changed diff --git a/Makefile b/Makefile index e0449d4d..561f9220 100644 --- a/Makefile +++ b/Makefile @@ -22,9 +22,9 @@ SHA_SHORT := $(shell git rev-parse --short HEAD) TAGS = "" MOD_NAME = github.com/rocboss/paopao-ce -LDFLAGS = -X "${MOD_NAME}/pkg/debug.version=${BUILD_VERSION}" \ - -X "${MOD_NAME}/pkg/debug.buildDate=${BUILD_DATE}" \ - -X "${MOD_NAME}/pkg/debug.commitID=${SHA_SHORT}" -w -s +LDFLAGS = -X "${MOD_NAME}/pkg/version.version=${BUILD_VERSION}" \ + -X "${MOD_NAME}/pkg/version.buildDate=${BUILD_DATE}" \ + -X "${MOD_NAME}/pkg/version.commitID=${SHA_SHORT}" -w -s all: fmt build diff --git a/README.md b/README.md index 1d504d8a..c65a47a0 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,7 @@ release/paopao-ce --no-default-features --features sqlite3,localoss,loggerfile,r |`Zinc` | 搜索 | 稳定(推荐) | 基于[Zinc](https://github.com/zinclabs/zinc)搜索引擎提供推文搜索服务 | |`Meili` | 搜索 | 稳定(推荐) | 基于[Meilisearch](https://github.com/meilisearch/meilisearch)搜索引擎提供推文搜索服务 | |`Bleve` | 搜索 | WIP | 基于[Bleve](https://github.com/blevesearch/bleve)搜索引擎提供推文搜索服务 | +|[`Sentry`](docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计) | 监控 | 内测 | 使用Sentry进行错误跟踪与性能监控 | |`LoggerFile` | 日志 | 稳定 | 使用文件写日志 | |`LoggerZinc` | 日志 | 稳定(推荐) | 使用[Zinc](https://github.com/zinclabs/zinc)写日志 | |`LoggerMeili` | 日志 | 内测 | 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志 | diff --git a/ROADMAP.md b/ROADMAP.md index 8d423b44..4b29771c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,6 +7,7 @@ * [ ] add `Auth:Bcrypt` feature * [ ] add `Auth:MD5` feature (just for compatible) * [x] add `RedisCacheIndex` feature +* [x] add `Sentry` feature * [x] add extend base ORM code for implement data logic base sqlx/sqlc * [ ] optimize media tweet submit logic * [ ] optimize search logic service diff --git a/docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计.md b/docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计.md new file mode 100644 index 00000000..e687de2f --- /dev/null +++ b/docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计.md @@ -0,0 +1,49 @@ +| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 | +| ----- | ----- | ----- | ----- | ----- | ----- | +| 23040412| 北野 | 2023-04-04 | 2023-04-04 | v1.0 | 提议 | + +### 概述 +[Sentry](https://github.com/getsentry/sentry) Sentry is a developer-first error tracking and performance monitoring platform that helps developers see what actually matters, solve quicker, and learn continuously about their applications. + +### 需求 +* 通过配置文件开启Sentry功能 + +### 方案 +#### 设计要点 +* config.yaml中添加`Sentry` 功能来启用Sentry功能 + +#### 设计细节 +* 参考实现(PR): +[add Sentry feature support #258](https://github.com/rocboss/paopao-ce/pull/258) + +### 疑问 + +1. 为什么要引入Sentry? +添加一种对paopao-ce的错误追踪与性能检测机制。 + +2. 如何开启这个功能? +* 在配置文件config.yaml中的`Features`中添加`Sentry`功能项开启该功能: + ```yaml + ... + # features中加上 Sentry + Features: + Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "MinIO", "Sentry"] + Base: ["Redis", "PhoneBind"] + Sentry: + Sentry: # Sentry配置 + Dsn: "http://4ea0af5cd88d4512b7e52070506c80ec@localhost:9000/2" + Debug: True + AttachStacktrace: True + TracesSampleRate: 1.0 + AttachLogrus: True # logrus是否附加到Sentry + AttachGin: True # gin是否附加到Sentry + ... + ``` + +### 参考文档 +* [sentry](https://github.com/getsentry/sentry) +* [self-hosted](https://develop.sentry.dev/self-hosted/) + +### 更新记录 +#### v1.0(2023-04-04) - 北野 +* 初始文档 diff --git a/features-status.md b/features-status.md index 6f1b29b5..f7434db1 100644 --- a/features-status.md +++ b/features-status.md @@ -123,6 +123,7 @@ * [ ] 提按文档 * [ ] 接口定义 * [ ] 业务逻辑实现 + #### 日志: * `LoggerFile` 使用文件写日志(目前状态: 稳定); * [ ] 提按文档 @@ -137,6 +138,12 @@ * [x] 接口定义 * [x] 业务逻辑实现 +#### 监控: +* `Sentry` 使用Sentry进行错误跟踪与性能监控(目前状态: 内测); + * [x] [提按文档](docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计) + * [x] 接口定义 + * [x] 业务逻辑实现 + #### 关系模式: * `Friendship` 弱关系好友模式,类似微信朋友圈(目前状态: 内测); * [x] [提按文档](docs/proposal/002-关于Friendship功能项的设计.md) diff --git a/go.mod b/go.mod index 6040e28c..b251a4fb 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/cockroachdb/errors v1.9.1 github.com/disintegration/imaging v1.6.2 github.com/fatih/color v1.15.0 + github.com/getsentry/sentry-go v0.20.0 github.com/gin-contrib/cors v1.4.0 github.com/gin-gonic/gin v1.9.0 github.com/go-resty/resty/v2 v2.7.0 @@ -49,14 +50,13 @@ require ( ) require ( - github.com/andybalholm/brotli v1.0.4 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/clbanning/mxj v1.8.4 // indirect github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect github.com/cockroachdb/redact v1.1.3 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/getsentry/sentry-go v0.12.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -113,16 +113,16 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d // indirect + github.com/valyala/fasthttp v1.40.0 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.6.0 // indirect + golang.org/x/crypto v0.7.0 // indirect golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect - golang.org/x/time v0.1.0 // indirect + golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index fa43de03..4b54e09f 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,9 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible h1:KpbJFXwhVeuxNtBJ74MCG github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI= github.com/allegro/bigcache/v3 v3.0.2/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY= github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= @@ -464,8 +465,9 @@ github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.20.0 h1:bwXW98iMRIWxn+4FgPW7vMrjmbym6HblXALmhjHmQaQ= +github.com/getsentry/sentry-go v0.20.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= @@ -478,8 +480,8 @@ github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -1280,8 +1282,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= @@ -1316,8 +1318,9 @@ github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKn github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d h1:xS9QTPgKl9ewGsAOPc+xW7DeStJDqYPfisDmeSCcbco= github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= +github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= @@ -1452,8 +1455,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1777,8 +1781,8 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/conf/conf.go b/internal/conf/conf.go index bcb2e3a6..75d4bc4c 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -17,6 +17,7 @@ var ( loggerFileSetting *LoggerFileSettingS loggerZincSetting *LoggerZincSettingS loggerMeiliSetting *LoggerMeiliSettingS + sentrySetting *SentrySettingS redisSetting *RedisSettingS PyroscopeSetting *PyroscopeSettingS @@ -83,6 +84,7 @@ func setupSetting(suite []string, noDefault bool) error { "Alipay": &AlipaySetting, "SmsJuhe": &SmsJuheSetting, "Pyroscope": &PyroscopeSetting, + "Sentry": &sentrySetting, "Logger": &loggerSetting, "LoggerFile": &loggerFileSetting, "LoggerZinc": &loggerZincSetting, @@ -126,6 +128,7 @@ func Initialize(suite []string, noDefault bool) { } setupLogger() + initSentry() } func GetOssDomain() string { @@ -159,3 +162,7 @@ func GetOssDomain() string { func RunMode() string { return AppSetting.RunMode } + +func UseSentryGin() bool { + return cfg.If("Sentry") && sentrySetting.AttachGin +} diff --git a/internal/conf/config.yaml b/internal/conf/config.yaml index d9adc5eb..2732591f 100644 --- a/internal/conf/config.yaml +++ b/internal/conf/config.yaml @@ -82,6 +82,13 @@ Pyroscope: # Pyroscope配置 Endpoint: "http://localhost:4040" # Pyroscope server address AuthToken: # Pyroscope authentication token Logger: none # Pyroscope logger (standard | logrus | none) +Sentry: # Sentry配置 + Dsn: "http://4ea0af5cd88d4512b7e52070506c80ec@localhost:9000/2" + Debug: True + AttachStacktrace: True + TracesSampleRate: 1.0 + AttachLogrus: True # logrus是否附加到Sentry + AttachGin: True # gin是否附加到Sentry Logger: # 日志通用配置 Level: debug # 日志级别 panic|fatal|error|warn|info|debug|trace LoggerFile: # 使用File写日志 diff --git a/internal/conf/logger.go b/internal/conf/logger.go index cbe3cc1b..f6d08723 100644 --- a/internal/conf/logger.go +++ b/internal/conf/logger.go @@ -6,8 +6,11 @@ package conf import ( "io" + "time" "github.com/alimy/cfg" + "github.com/getsentry/sentry-go" + sentrylogrus "github.com/getsentry/sentry-go/logrus" "github.com/sirupsen/logrus" "gopkg.in/natefinch/lumberjack.v2" ) @@ -42,3 +45,16 @@ func setupLogger() { }, }) } + +func setupSentryLogrus(opts sentry.ClientOptions) { + // Send only ERROR and higher level logs to Sentry + sentryLevels := []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel} + sentryHook, err := sentrylogrus.New(sentryLevels, opts) + if err != nil { + panic(err) + } + logrus.AddHook(sentryHook) + // Flushes before calling os.Exit(1) when using logger.Fatal + // (else all defers are not called, and Sentry does not have time to send the event) + logrus.RegisterExitHandler(func() { sentryHook.Flush(5 * time.Second) }) +} diff --git a/internal/conf/sentry.go b/internal/conf/sentry.go new file mode 100644 index 00000000..70c534e4 --- /dev/null +++ b/internal/conf/sentry.go @@ -0,0 +1,35 @@ +// Copyright 2023 ROC. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package conf + +import ( + "time" + + "github.com/alimy/cfg" + "github.com/getsentry/sentry-go" + "github.com/rocboss/paopao-ce/pkg/version" +) + +func initSentry() { + cfg.Be("Sentry", func() { + opts := sentry.ClientOptions{ + Dsn: sentrySetting.Dsn, + Debug: sentrySetting.Debug, + AttachStacktrace: sentrySetting.AttachStacktrace, + TracesSampleRate: sentrySetting.TracesSampleRate, + } + _ = sentry.Init(opts) + if sentrySetting.AttachLogrus { + setupSentryLogrus(opts) + } + sentry.WithScope(func(scope *sentry.Scope) { + scope.SetExtras(map[string]any{ + "version": version.VersionInfo(), + "time": time.Now().Local(), + }) + sentry.CaptureMessage("paopao-ce sentry works!") + }) + }) +} diff --git a/internal/conf/settting.go b/internal/conf/settting.go index 3766b51a..c2e40e4a 100644 --- a/internal/conf/settting.go +++ b/internal/conf/settting.go @@ -30,6 +30,15 @@ type PyroscopeSettingS struct { Logger string } +type SentrySettingS struct { + Dsn string + Debug bool + AttachStacktrace bool + TracesSampleRate float64 + AttachLogrus bool + AttachGin bool +} + type LoggerSettingS struct { Level string } diff --git a/internal/model/web/pub.go b/internal/model/web/pub.go index e4396247..a5634549 100644 --- a/internal/model/web/pub.go +++ b/internal/model/web/pub.go @@ -7,7 +7,7 @@ package web import ( "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/servants/base" - "github.com/rocboss/paopao-ce/pkg/debug" + "github.com/rocboss/paopao-ce/pkg/version" ) const ( @@ -55,7 +55,7 @@ type SendCaptchaReq struct { } type VersionResp struct { - BuildInfo *debug.BuildInfo `json:"build_info"` + BuildInfo *version.BuildInfo `json:"build_info"` } type LoginReq struct { diff --git a/internal/servants/admin/user.go b/internal/servants/admin/user.go index fef348cf..a35ea28f 100644 --- a/internal/servants/admin/user.go +++ b/internal/servants/admin/user.go @@ -37,7 +37,7 @@ func newUserSrv() api.User { func newUserBinding() api.UserBinding { return &userBinding{ UnimplementedUserBinding: &api.UnimplementedUserBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/base/base.go b/internal/servants/base/base.go index c27f4bd0..eb77d2f2 100644 --- a/internal/servants/base/base.go +++ b/internal/servants/base/base.go @@ -11,7 +11,11 @@ import ( "net/http" "github.com/alimy/mir/v3" + "github.com/cockroachdb/errors" + "github.com/getsentry/sentry-go" + sentrygin "github.com/getsentry/sentry-go/gin" "github.com/gin-gonic/gin" + "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/dao" "github.com/rocboss/paopao-ce/internal/dao/cache" @@ -39,6 +43,10 @@ type JsonResp struct { Data any `json:"data,omitempty"` } +type SentryHubSetter interface { + SetSentryHub(hub *sentry.Hub) +} + type UserSetter interface { SetUser(*core.User) } @@ -75,7 +83,7 @@ func UserNameFrom(c *gin.Context) (string, bool) { return "", false } -func BindAny(c *gin.Context, obj any) mir.Error { +func bindAny(c *gin.Context, obj any) mir.Error { var errs xerror.ValidErrors err := c.ShouldBind(obj) if err != nil { @@ -99,6 +107,40 @@ func BindAny(c *gin.Context, obj any) mir.Error { return nil } +func bindAnySentry(c *gin.Context, obj any) mir.Error { + hub := sentrygin.GetHubFromContext(c) + var errs xerror.ValidErrors + err := c.ShouldBind(obj) + if err != nil { + xerr := mir.NewError(xerror.InvalidParams.StatusCode(), xerror.InvalidParams.WithDetails(errs.Error())) + if hub != nil { + hub.CaptureException(errors.Wrap(xerr, "bind object")) + } + return xerr + } + // setup sentry hub if needed + if setter, ok := obj.(SentryHubSetter); ok && hub != nil { + setter.SetSentryHub(hub) + } + // setup *core.User if needed + if setter, ok := obj.(UserSetter); ok { + user, _ := UserFrom(c) + setter.SetUser(user) + } + // setup UserId if needed + if setter, ok := obj.(UserIdSetter); ok { + uid, _ := UserIdFrom(c) + setter.SetUserId(uid) + } + // setup PageInfo if needed + if setter, ok := obj.(PageInfoSetter); ok { + page, pageSize := app.GetPageInfo(c) + setter.SetPageInfo(page, pageSize) + } + return nil + +} + func RenderAny(c *gin.Context, data any, err mir.Error) { if err == nil { c.JSON(http.StatusOK, &JsonResp{ @@ -218,3 +260,10 @@ func NewDaoServant() *DaoServant { Ts: dao.TweetSearchService(), } } + +func NewBindAnyFn() func(c *gin.Context, obj any) mir.Error { + if conf.UseSentryGin() { + return bindAnySentry + } + return bindAny +} diff --git a/internal/servants/bot/user.go b/internal/servants/bot/user.go index aae9048c..2d7e2d51 100644 --- a/internal/servants/bot/user.go +++ b/internal/servants/bot/user.go @@ -37,7 +37,7 @@ func newUserSrv() api.User { func newUserBinding() api.UserBinding { return &userBinding{ UnimplementedUserBinding: &api.UnimplementedUserBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/localoss/user.go b/internal/servants/localoss/user.go index 37fc715e..adb65933 100644 --- a/internal/servants/localoss/user.go +++ b/internal/servants/localoss/user.go @@ -37,7 +37,7 @@ func newUserSrv() api.User { func newUserBinding() api.UserBinding { return &userBinding{ UnimplementedUserBinding: &api.UnimplementedUserBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/space/user.go b/internal/servants/space/user.go index d0e5c632..23e02b79 100644 --- a/internal/servants/space/user.go +++ b/internal/servants/space/user.go @@ -37,7 +37,7 @@ func newUserSrv() api.User { func newUserBinding() api.UserBinding { return &userBinding{ UnimplementedUserBinding: &api.UnimplementedUserBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/admin.go b/internal/servants/web/admin.go index 6a6dc2ec..4db8a9b9 100644 --- a/internal/servants/web/admin.go +++ b/internal/servants/web/admin.go @@ -59,7 +59,7 @@ func newAdminSrv(s *base.DaoServant) api.Admin { func newAdminBinding() api.AdminBinding { return &adminBinding{ UnimplementedAdminBinding: &api.UnimplementedAdminBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/alipay.go b/internal/servants/web/alipay.go index a111a1c6..5b4c35a3 100644 --- a/internal/servants/web/alipay.go +++ b/internal/servants/web/alipay.go @@ -200,7 +200,7 @@ func newAlipayPubSrv(s *base.DaoServant) api.AlipayPub { func newAlipayPubBinding(alipayClient *alipay.Client) api.AlipayPubBinding { return &alipayPubBinding{ UnimplementedAlipayPubBinding: &api.UnimplementedAlipayPubBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, alipayClient: alipayClient, } @@ -224,7 +224,7 @@ func newAlipayPrivSrv(s *base.DaoServant, client *alipay.Client) api.AlipayPriv func newAlipayPrivBinding() api.AlipayPrivBinding { return &alipayPrivBinding{ UnimplementedAlipayPrivBinding: &api.UnimplementedAlipayPrivBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/core.go b/internal/servants/web/core.go index ab9ebe55..982c27c8 100644 --- a/internal/servants/web/core.go +++ b/internal/servants/web/core.go @@ -450,7 +450,7 @@ func newCoreSrv(s *base.DaoServant, oss core.ObjectStorageService) api.Core { func newCoreBinding() api.CoreBinding { return &coreBinding{ UnimplementedCoreBinding: &api.UnimplementedCoreBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/followship.go b/internal/servants/web/followship.go index 95d19ec3..2aeaf9cc 100644 --- a/internal/servants/web/followship.go +++ b/internal/servants/web/followship.go @@ -41,7 +41,7 @@ func newFollowshipSrv(s *base.DaoServant) api.Followship { func newFollowshipBinding() api.FollowshipBinding { return &followshipBinding{ UnimplementedFollowshipBinding: &api.UnimplementedFollowshipBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/friendship.go b/internal/servants/web/friendship.go index 54c239ba..11f8e129 100644 --- a/internal/servants/web/friendship.go +++ b/internal/servants/web/friendship.go @@ -128,7 +128,7 @@ func newFriendshipSrv(s *base.DaoServant) api.Friendship { func newFriendshipBinding() api.FriendshipBinding { return &friendshipBinding{ UnimplementedFriendshipBinding: &api.UnimplementedFriendshipBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/loose.go b/internal/servants/web/loose.go index 9e9b1bff..a619fd26 100644 --- a/internal/servants/web/loose.go +++ b/internal/servants/web/loose.go @@ -158,7 +158,7 @@ func newLooseSrv(s *base.DaoServant) api.Loose { func newLooseBinding() api.LooseBinding { return &looseBinding{ UnimplementedLooseBinding: &api.UnimplementedLooseBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/priv.go b/internal/servants/web/priv.go index 3abdb23c..0d4df502 100644 --- a/internal/servants/web/priv.go +++ b/internal/servants/web/priv.go @@ -877,7 +877,7 @@ func newPrivSrv(s *base.DaoServant, oss core.ObjectStorageService) api.Priv { func newPrivBinding() api.PrivBinding { return &privBinding{ UnimplementedPrivBinding: &api.UnimplementedPrivBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/servants/web/pub.go b/internal/servants/web/pub.go index 6fcbf6cb..34626ae2 100644 --- a/internal/servants/web/pub.go +++ b/internal/servants/web/pub.go @@ -25,8 +25,8 @@ import ( "github.com/rocboss/paopao-ce/internal/servants/web/assets" "github.com/rocboss/paopao-ce/pkg/app" "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/debug" "github.com/rocboss/paopao-ce/pkg/utils" + "github.com/rocboss/paopao-ce/pkg/version" "github.com/rocboss/paopao-ce/pkg/xerror" "github.com/sirupsen/logrus" ) @@ -319,7 +319,7 @@ func (s *pubSrv) Login(req *web.LoginReq) (*web.LoginResp, mir.Error) { func (s *pubSrv) Version() (*web.VersionResp, mir.Error) { return &web.VersionResp{ - BuildInfo: debug.ReadBuildInfo(), + BuildInfo: version.ReadBuildInfo(), }, nil } @@ -351,7 +351,7 @@ func newPubSrv(s *base.DaoServant) api.Pub { func newPubBinding() api.PubBinding { return &pubBinding{ UnimplementedPubBinding: &api.UnimplementedPubBinding{ - BindAny: base.BindAny, + BindAny: base.NewBindAnyFn(), }, } } diff --git a/internal/service/web.go b/internal/service/web.go index 925c03c2..c158f732 100644 --- a/internal/service/web.go +++ b/internal/service/web.go @@ -10,6 +10,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/fatih/color" + sentrygin "github.com/getsentry/sentry-go/gin" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/internal/conf" @@ -52,6 +53,12 @@ func newWebEngine() *gin.Engine { corsConfig.AllowAllOrigins = true corsConfig.AddAllowHeaders("Authorization") e.Use(cors.New(corsConfig)) + // 使用Sentry hook + if conf.UseSentryGin() { + e.Use(sentrygin.New(sentrygin.Options{ + Repanic: true, + })) + } // 默认404 e.NoRoute(func(c *gin.Context) { diff --git a/main.go b/main.go index 6e68e783..12e929fb 100644 --- a/main.go +++ b/main.go @@ -11,13 +11,17 @@ import ( "os/signal" "strings" "syscall" + "time" + "github.com/alimy/cfg" "github.com/fatih/color" + "github.com/getsentry/sentry-go" "github.com/rocboss/paopao-ce/internal" "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/service" "github.com/rocboss/paopao-ce/pkg/debug" "github.com/rocboss/paopao-ce/pkg/utils" + "github.com/rocboss/paopao-ce/pkg/version" "github.com/sourcegraph/conc" _ "go.uber.org/automaxprocs" ) @@ -47,6 +51,13 @@ func init() { internal.Initialize() } +func deferFn() { + if cfg.If("Sentry") { + // Flush buffered events before the program terminates. + sentry.Flush(2 * time.Second) + } +} + func flagParse() { flag.BoolVar(&noDefaultFeatures, "no-default-features", false, "whether not use default features") flag.Var(&features, "features", "use special features") @@ -54,13 +65,16 @@ func flagParse() { } func main() { - utils.PrintHelloBanner(debug.VersionInfo()) + utils.PrintHelloBanner(version.VersionInfo()) ss := service.MustInitService() if len(ss) < 1 { fmt.Fprintln(color.Output, "no service need start so just exit") return } + // do defer function + defer deferFn() + // start pyroscope if need debug.StartPyroscope() diff --git a/pkg/debug/version.go b/pkg/version/version.go similarity index 97% rename from pkg/debug/version.go rename to pkg/version/version.go index 12623f96..b3683bef 100644 --- a/pkg/debug/version.go +++ b/pkg/version/version.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. -package debug +package version import ( "fmt"