mirror of
https://github.com/ccfos/nightingale.git
synced 2026-03-03 22:48:56 +00:00
Compare commits
1 Commits
api-add-tr
...
dev23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b49ab44818 |
@@ -54,6 +54,7 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
service.POST("/make-event", rt.makeEvent)
|
||||
service.GET("/event-detail/:hash", rt.eventDetail)
|
||||
service.GET("/alert-eval-detail/:id", rt.alertEvalDetail)
|
||||
service.GET("/trace-logs/:traceid", rt.traceLogs)
|
||||
}
|
||||
|
||||
func Render(c *gin.Context, data, msg interface{}) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/loggrep"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) alertEvalDetail(c *gin.Context) {
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/queue"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/poster"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/loggrep"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) eventDetail(c *gin.Context) {
|
||||
|
||||
28
alert/router/router_trace_logs.go
Normal file
28
alert/router/router_trace_logs.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/loggrep"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (rt *Router) traceLogs(c *gin.Context) {
|
||||
traceId := ginx.UrlParamStr(c, "traceid")
|
||||
if !loggrep.IsValidTraceID(traceId) {
|
||||
ginx.Bomb(200, "invalid trace id format")
|
||||
}
|
||||
|
||||
instance := fmt.Sprintf("%s:%d", rt.Alert.Heartbeat.IP, rt.HTTP.Port)
|
||||
|
||||
keyword := "trace_id=" + traceId
|
||||
logs, err := loggrep.GrepLatestLogFiles(rt.LogDir, keyword)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
ginx.NewRender(c).Data(loggrep.EventDetailResp{
|
||||
Logs: logs,
|
||||
Instance: instance,
|
||||
}, nil)
|
||||
}
|
||||
@@ -24,11 +24,11 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/prom"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/idents"
|
||||
"github.com/ccfos/nightingale/v6/storage"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rakyll/statik/fs"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"github.com/toolkits/pkg/runner"
|
||||
)
|
||||
@@ -421,6 +421,7 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
pages.GET("/event-notify-records/:eid", rt.notificationRecordList)
|
||||
pages.GET("/event-detail/:hash", rt.eventDetailPage)
|
||||
pages.GET("/alert-eval-detail/:id", rt.alertEvalDetailPage)
|
||||
pages.GET("/trace-logs/:traceid", rt.traceLogsPage)
|
||||
|
||||
// card logic
|
||||
pages.GET("/alert-cur-events/list", rt.auth(), rt.user(), rt.alertCurEventsList)
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
// no param
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/loggrep"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
// alertEvalDetailPage renders an HTML log viewer page for alert rule evaluation logs.
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
@@ -16,12 +16,12 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/pconf"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/writer"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/common"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/file"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"github.com/toolkits/pkg/runner"
|
||||
)
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package router
|
||||
import (
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/prom"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) metricFilterGets(c *gin.Context) {
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/center/integration"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/ccfos/nightingale/v6/center/integration"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/storage"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
captcha "github.com/mojocn/base64Captcha"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) chartShareGets(c *gin.Context) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) notifyChannelsGets(c *gin.Context) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
const EMBEDDEDDASHBOARD = "embedded-dashboards"
|
||||
|
||||
@@ -2,9 +2,9 @@ package router
|
||||
|
||||
import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/secu"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
type confPropCrypto struct {
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func checkAnnotationPermission(c *gin.Context, ctx *ctx.Context, dashboardId int64) {
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/datasource/opensearch"
|
||||
"github.com/ccfos/nightingale/v6/dskit/clickhouse"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/dscache"
|
||||
"github.com/ccfos/nightingale/v6/dskit/types"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
func (rt *Router) ShowDatabases(c *gin.Context) {
|
||||
@@ -18,7 +18,7 @@ func (rt *Router) ShowDatabases(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(c.Request.Context(), "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func (rt *Router) ShowTables(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(c.Request.Context(), "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (rt *Router) DescribeTable(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(c.Request.Context(), "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
// 只接受一个入参
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) embeddedProductGets(c *gin.Context) {
|
||||
|
||||
@@ -3,10 +3,10 @@ package router
|
||||
import (
|
||||
"github.com/ccfos/nightingale/v6/datasource/es"
|
||||
"github.com/ccfos/nightingale/v6/dscache"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
type IndexReq struct {
|
||||
@@ -34,7 +34,7 @@ func (rt *Router) QueryIndices(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(c.Request.Context(), "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func (rt *Router) QueryFields(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(c.Request.Context(), "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (rt *Router) QueryESVariable(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(c.Request.Context(), "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
// 创建 ES Index Pattern
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/naming"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/loggrep"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
// eventDetailPage renders an HTML log viewer page (for pages group).
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/alert/pipeline/engine"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
const defaultLimit = 300
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/idents"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/dingtalk"
|
||||
"github.com/ccfos/nightingale/v6/pkg/feishu"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ldapx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/oauth2x"
|
||||
"github.com/ccfos/nightingale/v6/pkg/oidcx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/secu"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -37,7 +37,9 @@ type loginForm struct {
|
||||
func (rt *Router) loginPost(c *gin.Context) {
|
||||
var f loginForm
|
||||
ginx.BindJSON(c, &f)
|
||||
logger.Infof("username:%s login from:%s", f.Username, c.ClientIP())
|
||||
|
||||
rctx := c.Request.Context()
|
||||
logx.Infof(rctx, "username:%s login from:%s", f.Username, c.ClientIP())
|
||||
|
||||
if rt.HTTP.ShowCaptcha.Enable {
|
||||
if !CaptchaVerify(f.Captchaid, f.Verifyvalue) {
|
||||
@@ -50,23 +52,25 @@ func (rt *Router) loginPost(c *gin.Context) {
|
||||
if rt.HTTP.RSA.OpenRSA {
|
||||
decPassWord, err := secu.Decrypt(f.Password, rt.HTTP.RSA.RSAPrivateKey, rt.HTTP.RSA.RSAPassWord)
|
||||
if err != nil {
|
||||
logger.Errorf("RSA Decrypt failed: %v username: %s", err, f.Username)
|
||||
logx.Errorf(rctx, "RSA Decrypt failed: %v username: %s", err, f.Username)
|
||||
ginx.NewRender(c).Message(err)
|
||||
return
|
||||
}
|
||||
authPassWord = decPassWord
|
||||
}
|
||||
|
||||
reqCtx := rt.Ctx.WithContext(rctx)
|
||||
|
||||
var user *models.User
|
||||
var err error
|
||||
lc := rt.Sso.LDAP.Copy()
|
||||
if lc.Enable {
|
||||
user, err = ldapx.LdapLogin(rt.Ctx, f.Username, authPassWord, lc.DefaultRoles, lc.DefaultTeams, lc)
|
||||
user, err = ldapx.LdapLogin(reqCtx, f.Username, authPassWord, lc.DefaultRoles, lc.DefaultTeams, lc)
|
||||
if err != nil {
|
||||
logger.Debugf("ldap login failed: %v username: %s", err, f.Username)
|
||||
logx.Debugf(rctx, "ldap login failed: %v username: %s", err, f.Username)
|
||||
var errLoginInN9e error
|
||||
// to use n9e as the minimum guarantee for login
|
||||
if user, errLoginInN9e = models.PassLogin(rt.Ctx, rt.Redis, f.Username, authPassWord); errLoginInN9e != nil {
|
||||
if user, errLoginInN9e = models.PassLogin(reqCtx, rt.Redis, f.Username, authPassWord); errLoginInN9e != nil {
|
||||
ginx.NewRender(c).Message("ldap login failed: %v; n9e login failed: %v", err, errLoginInN9e)
|
||||
return
|
||||
}
|
||||
@@ -74,7 +78,7 @@ func (rt *Router) loginPost(c *gin.Context) {
|
||||
user.RolesLst = strings.Fields(user.Roles)
|
||||
}
|
||||
} else {
|
||||
user, err = models.PassLogin(rt.Ctx, rt.Redis, f.Username, authPassWord)
|
||||
user, err = models.PassLogin(reqCtx, rt.Redis, f.Username, authPassWord)
|
||||
ginx.Dangerous(err)
|
||||
}
|
||||
|
||||
@@ -98,7 +102,8 @@ func (rt *Router) loginPost(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (rt *Router) logoutPost(c *gin.Context) {
|
||||
logger.Infof("username:%s logout from:%s", c.GetString("username"), c.ClientIP())
|
||||
rctx := c.Request.Context()
|
||||
logx.Infof(rctx, "username:%s logout from:%s", c.GetString("username"), c.ClientIP())
|
||||
metadata, err := rt.extractTokenMetadata(c.Request)
|
||||
if err != nil {
|
||||
ginx.NewRender(c, http.StatusBadRequest).Message("failed to parse jwt token")
|
||||
@@ -117,7 +122,7 @@ func (rt *Router) logoutPost(c *gin.Context) {
|
||||
// 获取用户的 id_token
|
||||
idToken, err := rt.fetchIdToken(c.Request.Context(), user.Id)
|
||||
if err != nil {
|
||||
logger.Debugf("fetch id_token failed: %v, user_id: %d", err, user.Id)
|
||||
logx.Debugf(rctx, "fetch id_token failed: %v, user_id: %d", err, user.Id)
|
||||
idToken = "" // 如果获取失败,使用空字符串
|
||||
}
|
||||
|
||||
@@ -220,7 +225,7 @@ func (rt *Router) refreshPost(c *gin.Context) {
|
||||
// 注意:这里不会获取新的 id_token,只是延长 Redis 中现有 id_token 的 TTL
|
||||
if idToken, err := rt.fetchIdToken(c.Request.Context(), userid); err == nil && idToken != "" {
|
||||
if err := rt.saveIdToken(c.Request.Context(), userid, idToken); err != nil {
|
||||
logger.Debugf("refresh id_token ttl failed: %v, user_id: %d", err, userid)
|
||||
logx.Debugf(c.Request.Context(), "refresh id_token ttl failed: %v, user_id: %d", err, userid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,12 +276,13 @@ type CallbackOutput struct {
|
||||
}
|
||||
|
||||
func (rt *Router) loginCallback(c *gin.Context) {
|
||||
rctx := c.Request.Context()
|
||||
code := ginx.QueryStr(c, "code", "")
|
||||
state := ginx.QueryStr(c, "state", "")
|
||||
|
||||
ret, err := rt.Sso.OIDC.Callback(rt.Redis, c.Request.Context(), code, state)
|
||||
ret, err := rt.Sso.OIDC.Callback(rt.Redis, rctx, code, state)
|
||||
if err != nil {
|
||||
logger.Errorf("sso_callback fail. code:%s, state:%s, get ret: %+v. error: %v", code, state, ret, err)
|
||||
logx.Errorf(rctx, "sso_callback fail. code:%s, state:%s, get ret: %+v. error: %v", code, state, ret, err)
|
||||
ginx.NewRender(c).Data(CallbackOutput{}, err)
|
||||
return
|
||||
}
|
||||
@@ -299,7 +305,7 @@ func (rt *Router) loginCallback(c *gin.Context) {
|
||||
for _, gid := range rt.Sso.OIDC.DefaultTeams {
|
||||
err = models.UserGroupMemberAdd(rt.Ctx, gid, user.Id)
|
||||
if err != nil {
|
||||
logger.Errorf("user:%v UserGroupMemberAdd: %s", user, err)
|
||||
logx.Errorf(rctx, "user:%v UserGroupMemberAdd: %s", user, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,12 +315,12 @@ func (rt *Router) loginCallback(c *gin.Context) {
|
||||
userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username)
|
||||
ts, err := rt.createTokens(rt.HTTP.JWTAuth.SigningKey, userIdentity)
|
||||
ginx.Dangerous(err)
|
||||
ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts))
|
||||
ginx.Dangerous(rt.createAuth(rctx, userIdentity, ts))
|
||||
|
||||
// 保存 id_token 到 Redis,用于登出时使用
|
||||
if ret.IdToken != "" {
|
||||
if err := rt.saveIdToken(c.Request.Context(), user.Id, ret.IdToken); err != nil {
|
||||
logger.Errorf("save id_token failed: %v, user_id: %d", err, user.Id)
|
||||
if err := rt.saveIdToken(rctx, user.Id, ret.IdToken); err != nil {
|
||||
logx.Errorf(rctx, "save id_token failed: %v, user_id: %d", err, user.Id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +361,7 @@ func (rt *Router) loginRedirectCas(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !rt.Sso.CAS.Enable {
|
||||
logger.Error("cas is not enable")
|
||||
logx.Errorf(c.Request.Context(), "cas is not enable")
|
||||
ginx.NewRender(c).Data("", nil)
|
||||
return
|
||||
}
|
||||
@@ -370,17 +376,18 @@ func (rt *Router) loginRedirectCas(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (rt *Router) loginCallbackCas(c *gin.Context) {
|
||||
rctx := c.Request.Context()
|
||||
ticket := ginx.QueryStr(c, "ticket", "")
|
||||
state := ginx.QueryStr(c, "state", "")
|
||||
ret, err := rt.Sso.CAS.ValidateServiceTicket(c.Request.Context(), ticket, state, rt.Redis)
|
||||
ret, err := rt.Sso.CAS.ValidateServiceTicket(rctx, ticket, state, rt.Redis)
|
||||
if err != nil {
|
||||
logger.Errorf("ValidateServiceTicket: %s", err)
|
||||
logx.Errorf(rctx, "ValidateServiceTicket: %s", err)
|
||||
ginx.NewRender(c).Data("", err)
|
||||
return
|
||||
}
|
||||
user, err := models.UserGet(rt.Ctx, "username=?", ret.Username)
|
||||
if err != nil {
|
||||
logger.Errorf("UserGet: %s", err)
|
||||
logx.Errorf(rctx, "UserGet: %s", err)
|
||||
}
|
||||
ginx.Dangerous(err)
|
||||
if user != nil {
|
||||
@@ -399,10 +406,10 @@ func (rt *Router) loginCallbackCas(c *gin.Context) {
|
||||
userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username)
|
||||
ts, err := rt.createTokens(rt.HTTP.JWTAuth.SigningKey, userIdentity)
|
||||
if err != nil {
|
||||
logger.Errorf("createTokens: %s", err)
|
||||
logx.Errorf(rctx, "createTokens: %s", err)
|
||||
}
|
||||
ginx.Dangerous(err)
|
||||
ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts))
|
||||
ginx.Dangerous(rt.createAuth(rctx, userIdentity, ts))
|
||||
|
||||
redirect := "/"
|
||||
if ret.Redirect != "/login" {
|
||||
@@ -475,12 +482,13 @@ func (rt *Router) loginRedirectDingTalk(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (rt *Router) loginCallbackDingTalk(c *gin.Context) {
|
||||
rctx := c.Request.Context()
|
||||
code := ginx.QueryStr(c, "code", "")
|
||||
state := ginx.QueryStr(c, "state", "")
|
||||
|
||||
ret, err := rt.Sso.DingTalk.Callback(rt.Redis, c.Request.Context(), code, state)
|
||||
ret, err := rt.Sso.DingTalk.Callback(rt.Redis, rctx, code, state)
|
||||
if err != nil {
|
||||
logger.Errorf("sso_callback DingTalk fail. code:%s, state:%s, get ret: %+v. error: %v", code, state, ret, err)
|
||||
logx.Errorf(rctx, "sso_callback DingTalk fail. code:%s, state:%s, get ret: %+v. error: %v", code, state, ret, err)
|
||||
ginx.NewRender(c).Data(CallbackOutput{}, err)
|
||||
return
|
||||
}
|
||||
@@ -550,12 +558,13 @@ func (rt *Router) loginRedirectFeiShu(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (rt *Router) loginCallbackFeiShu(c *gin.Context) {
|
||||
rctx := c.Request.Context()
|
||||
code := ginx.QueryStr(c, "code", "")
|
||||
state := ginx.QueryStr(c, "state", "")
|
||||
|
||||
ret, err := rt.Sso.FeiShu.Callback(rt.Redis, c.Request.Context(), code, state)
|
||||
ret, err := rt.Sso.FeiShu.Callback(rt.Redis, rctx, code, state)
|
||||
if err != nil {
|
||||
logger.Errorf("sso_callback FeiShu fail. code:%s, state:%s, get ret: %+v. error: %v", code, state, ret, err)
|
||||
logx.Errorf(rctx, "sso_callback FeiShu fail. code:%s, state:%s, get ret: %+v. error: %v", code, state, ret, err)
|
||||
ginx.NewRender(c).Data(CallbackOutput{}, err)
|
||||
return
|
||||
}
|
||||
@@ -583,7 +592,7 @@ func (rt *Router) loginCallbackFeiShu(c *gin.Context) {
|
||||
if len(defaultUserGroups) > 0 {
|
||||
err = user.AddToUserGroups(rt.Ctx, defaultUserGroups)
|
||||
if err != nil {
|
||||
logger.Errorf("sso feishu add user group error %v", ret, err)
|
||||
logx.Errorf(rctx, "sso feishu add user group error %v %v", ret, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,12 +619,13 @@ func (rt *Router) loginCallbackFeiShu(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (rt *Router) loginCallbackOAuth(c *gin.Context) {
|
||||
rctx := c.Request.Context()
|
||||
code := ginx.QueryStr(c, "code", "")
|
||||
state := ginx.QueryStr(c, "state", "")
|
||||
|
||||
ret, err := rt.Sso.OAuth2.Callback(rt.Redis, c.Request.Context(), code, state)
|
||||
ret, err := rt.Sso.OAuth2.Callback(rt.Redis, rctx, code, state)
|
||||
if err != nil {
|
||||
logger.Debugf("sso.callback() get ret %+v error %v", ret, err)
|
||||
logx.Debugf(rctx, "sso.callback() get ret %+v error %v", ret, err)
|
||||
ginx.NewRender(c).Data(CallbackOutput{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/slice"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/tplx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) messageTemplatesAdd(c *gin.Context) {
|
||||
|
||||
@@ -2,9 +2,9 @@ package router
|
||||
|
||||
import (
|
||||
"github.com/ccfos/nightingale/v6/center/cconf"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) metricsDescGetFile(c *gin.Context) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
// no param
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/mute"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/center/cstats"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/sender"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) notifyChannelsAdd(c *gin.Context) {
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/memsto"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/tplx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/str"
|
||||
)
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/slice"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/center/cconf"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/tplx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/str"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ package router
|
||||
import (
|
||||
"github.com/ccfos/nightingale/v6/datasource/opensearch"
|
||||
"github.com/ccfos/nightingale/v6/dscache"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/poster"
|
||||
pkgprom "github.com/ccfos/nightingale/v6/pkg/prom"
|
||||
"github.com/ccfos/nightingale/v6/prom"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"github.com/toolkits/pkg/net/httplib"
|
||||
)
|
||||
@@ -38,15 +39,16 @@ func (rt *Router) promBatchQueryRange(c *gin.Context) {
|
||||
var f BatchQueryForm
|
||||
ginx.Dangerous(c.BindJSON(&f))
|
||||
|
||||
lst, err := PromBatchQueryRange(rt.PromClients, f)
|
||||
lst, err := PromBatchQueryRange(c.Request.Context(), rt.PromClients, f)
|
||||
ginx.NewRender(c).Data(lst, err)
|
||||
}
|
||||
|
||||
func PromBatchQueryRange(pc *prom.PromClientMap, f BatchQueryForm) ([]model.Value, error) {
|
||||
func PromBatchQueryRange(ctx context.Context, pc *prom.PromClientMap, f BatchQueryForm) ([]model.Value, error) {
|
||||
var lst []model.Value
|
||||
|
||||
cli := pc.GetCli(f.DatasourceId)
|
||||
if cli == nil {
|
||||
logx.Warningf(ctx, "no such datasource id: %d", f.DatasourceId)
|
||||
return lst, fmt.Errorf("no such datasource id: %d", f.DatasourceId)
|
||||
}
|
||||
|
||||
@@ -57,8 +59,9 @@ func PromBatchQueryRange(pc *prom.PromClientMap, f BatchQueryForm) ([]model.Valu
|
||||
Step: time.Duration(item.Step) * time.Second,
|
||||
}
|
||||
|
||||
resp, _, err := cli.QueryRange(context.Background(), item.Query, r)
|
||||
resp, _, err := cli.QueryRange(ctx, item.Query, r)
|
||||
if err != nil {
|
||||
logx.Warningf(ctx, "query range error: query:%s err:%v", item.Query, err)
|
||||
return lst, err
|
||||
}
|
||||
|
||||
@@ -81,22 +84,23 @@ func (rt *Router) promBatchQueryInstant(c *gin.Context) {
|
||||
var f BatchInstantForm
|
||||
ginx.Dangerous(c.BindJSON(&f))
|
||||
|
||||
lst, err := PromBatchQueryInstant(rt.PromClients, f)
|
||||
lst, err := PromBatchQueryInstant(c.Request.Context(), rt.PromClients, f)
|
||||
ginx.NewRender(c).Data(lst, err)
|
||||
}
|
||||
|
||||
func PromBatchQueryInstant(pc *prom.PromClientMap, f BatchInstantForm) ([]model.Value, error) {
|
||||
func PromBatchQueryInstant(ctx context.Context, pc *prom.PromClientMap, f BatchInstantForm) ([]model.Value, error) {
|
||||
var lst []model.Value
|
||||
|
||||
cli := pc.GetCli(f.DatasourceId)
|
||||
if cli == nil {
|
||||
logger.Warningf("no such datasource id: %d", f.DatasourceId)
|
||||
logx.Warningf(ctx, "no such datasource id: %d", f.DatasourceId)
|
||||
return lst, fmt.Errorf("no such datasource id: %d", f.DatasourceId)
|
||||
}
|
||||
|
||||
for _, item := range f.Queries {
|
||||
resp, _, err := cli.Query(context.Background(), item.Query, time.Unix(item.Time, 0))
|
||||
resp, _, err := cli.Query(ctx, item.Query, time.Unix(item.Time, 0))
|
||||
if err != nil {
|
||||
logx.Warningf(ctx, "query instant error: query:%s err:%v", item.Query, err)
|
||||
return lst, err
|
||||
}
|
||||
|
||||
@@ -189,7 +193,7 @@ func (rt *Router) dsProxy(c *gin.Context) {
|
||||
|
||||
modifyResponse := func(r *http.Response) error {
|
||||
if r.StatusCode == http.StatusUnauthorized {
|
||||
logger.Warningf("proxy path:%s unauthorized access ", c.Request.URL.Path)
|
||||
logx.Warningf(c.Request.Context(), "proxy path:%s unauthorized access ", c.Request.URL.Path)
|
||||
return fmt.Errorf("unauthorized access")
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/eval"
|
||||
"github.com/ccfos/nightingale/v6/dscache"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
type CheckDsPermFunc func(c *gin.Context, dsId int64, cate string, q interface{}) bool
|
||||
@@ -47,6 +47,7 @@ func QueryLogBatchConcurrently(anonymousAccess bool, ctx *gin.Context, f QueryFr
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
var errs []error
|
||||
rctx := ctx.Request.Context()
|
||||
|
||||
for _, q := range f.Queries {
|
||||
if !anonymousAccess && !CheckDsPerm(ctx, q.Did, q.DsCate, q) {
|
||||
@@ -55,14 +56,14 @@ func QueryLogBatchConcurrently(anonymousAccess bool, ctx *gin.Context, f QueryFr
|
||||
|
||||
plug, exists := dscache.DsCache.Get(q.DsCate, q.Did)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists query:%+v", q.Did, q)
|
||||
logx.Warningf(rctx, "cluster:%d not exists query:%+v", q.Did, q)
|
||||
return LogResp{}, fmt.Errorf("cluster not exists")
|
||||
}
|
||||
|
||||
// 根据数据源类型对 Query 进行模板渲染处理
|
||||
err := eval.ExecuteQueryTemplate(q.DsCate, q.Query, nil)
|
||||
if err != nil {
|
||||
logger.Warningf("query template execute error: %v", err)
|
||||
logx.Warningf(rctx, "query template execute error: %v", err)
|
||||
return LogResp{}, fmt.Errorf("query template execute error: %v", err)
|
||||
}
|
||||
|
||||
@@ -70,12 +71,12 @@ func QueryLogBatchConcurrently(anonymousAccess bool, ctx *gin.Context, f QueryFr
|
||||
go func(query Query) {
|
||||
defer wg.Done()
|
||||
|
||||
data, total, err := plug.QueryLog(ctx.Request.Context(), query.Query)
|
||||
data, total, err := plug.QueryLog(rctx, query.Query)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("query data error: %v query:%v\n ", err, query)
|
||||
logger.Warningf(errMsg)
|
||||
logx.Warningf(rctx, "%s", errMsg)
|
||||
errs = append(errs, err)
|
||||
return
|
||||
}
|
||||
@@ -121,6 +122,7 @@ func QueryDataConcurrently(anonymousAccess bool, ctx *gin.Context, f models.Quer
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
var errs []error
|
||||
rctx := ctx.Request.Context()
|
||||
|
||||
for _, q := range f.Queries {
|
||||
if !anonymousAccess && !CheckDsPerm(ctx, f.DatasourceId, f.Cate, q) {
|
||||
@@ -129,7 +131,7 @@ func QueryDataConcurrently(anonymousAccess bool, ctx *gin.Context, f models.Quer
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(rctx, "cluster:%d not exists", f.DatasourceId)
|
||||
return nil, fmt.Errorf("cluster not exists")
|
||||
}
|
||||
|
||||
@@ -137,16 +139,16 @@ func QueryDataConcurrently(anonymousAccess bool, ctx *gin.Context, f models.Quer
|
||||
go func(query interface{}) {
|
||||
defer wg.Done()
|
||||
|
||||
data, err := plug.QueryData(ctx.Request.Context(), query)
|
||||
data, err := plug.QueryData(rctx, query)
|
||||
if err != nil {
|
||||
logger.Warningf("query data error: req:%+v err:%v", query, err)
|
||||
logx.Warningf(rctx, "query data error: req:%+v err:%v", query, err)
|
||||
mu.Lock()
|
||||
errs = append(errs, err)
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debugf("query data: req:%+v resp:%+v", query, data)
|
||||
logx.Debugf(rctx, "query data: req:%+v resp:%+v", query, data)
|
||||
mu.Lock()
|
||||
resp = append(resp, data...)
|
||||
mu.Unlock()
|
||||
@@ -192,6 +194,7 @@ func QueryLogConcurrently(anonymousAccess bool, ctx *gin.Context, f models.Query
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
var errs []error
|
||||
rctx := ctx.Request.Context()
|
||||
|
||||
for _, q := range f.Queries {
|
||||
if !anonymousAccess && !CheckDsPerm(ctx, f.DatasourceId, f.Cate, q) {
|
||||
@@ -200,7 +203,7 @@ func QueryLogConcurrently(anonymousAccess bool, ctx *gin.Context, f models.Query
|
||||
|
||||
plug, exists := dscache.DsCache.Get(f.Cate, f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists query:%+v", f.DatasourceId, f)
|
||||
logx.Warningf(rctx, "cluster:%d not exists query:%+v", f.DatasourceId, f)
|
||||
return LogResp{}, fmt.Errorf("cluster not exists")
|
||||
}
|
||||
|
||||
@@ -208,11 +211,11 @@ func QueryLogConcurrently(anonymousAccess bool, ctx *gin.Context, f models.Query
|
||||
go func(query interface{}) {
|
||||
defer wg.Done()
|
||||
|
||||
data, total, err := plug.QueryLog(ctx.Request.Context(), query)
|
||||
logger.Debugf("query log: req:%+v resp:%+v", query, data)
|
||||
data, total, err := plug.QueryLog(rctx, query)
|
||||
logx.Debugf(rctx, "query log: req:%+v resp:%+v", query, data)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("query data error: %v query:%v\n ", err, query)
|
||||
logger.Warningf(errMsg)
|
||||
logx.Warningf(rctx, "%s", errMsg)
|
||||
mu.Lock()
|
||||
errs = append(errs, err)
|
||||
mu.Unlock()
|
||||
@@ -250,6 +253,7 @@ func (rt *Router) QueryLogV2(c *gin.Context) {
|
||||
func (rt *Router) QueryLog(c *gin.Context) {
|
||||
var f models.QueryParam
|
||||
ginx.BindJSON(c, &f)
|
||||
rctx := c.Request.Context()
|
||||
|
||||
var resp []interface{}
|
||||
for _, q := range f.Queries {
|
||||
@@ -259,13 +263,13 @@ func (rt *Router) QueryLog(c *gin.Context) {
|
||||
|
||||
plug, exists := dscache.DsCache.Get("elasticsearch", f.DatasourceId)
|
||||
if !exists {
|
||||
logger.Warningf("cluster:%d not exists", f.DatasourceId)
|
||||
logx.Warningf(rctx, "cluster:%d not exists", f.DatasourceId)
|
||||
ginx.Bomb(200, "cluster not exists")
|
||||
}
|
||||
|
||||
data, _, err := plug.QueryLog(c.Request.Context(), q)
|
||||
data, _, err := plug.QueryLog(rctx, q)
|
||||
if err != nil {
|
||||
logger.Warningf("query data error: %v", err)
|
||||
logx.Warningf(rctx, "query data error: %v", err)
|
||||
ginx.Bomb(200, "err:%v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) recordingRuleGets(c *gin.Context) {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/center/cconf"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) rolesGets(c *gin.Context) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/center/cconf"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/slice"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) savedViewGets(c *gin.Context) {
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/flashduty"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ormx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/secu"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) serversGet(c *gin.Context) {
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
// sourceTokenAdd 生成新的源令牌
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/idents"
|
||||
"github.com/ccfos/nightingale/v6/storage"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/alert/sender"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
"github.com/toolkits/pkg/str"
|
||||
)
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/datasource/tdengine"
|
||||
"github.com/ccfos/nightingale/v6/dscache"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
type databasesQueryForm struct {
|
||||
|
||||
136
center/router/router_trace_logs.go
Normal file
136
center/router/router_trace_logs.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/loggrep"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// traceLogsPage renders an HTML log viewer page for trace logs.
|
||||
func (rt *Router) traceLogsPage(c *gin.Context) {
|
||||
traceId := ginx.UrlParamStr(c, "traceid")
|
||||
if !loggrep.IsValidTraceID(traceId) {
|
||||
c.String(http.StatusBadRequest, "invalid trace id format")
|
||||
return
|
||||
}
|
||||
|
||||
logs, instance, err := rt.getTraceLogs(traceId)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
err = loggrep.RenderTraceLogsHTML(c.Writer, loggrep.TraceLogsPageData{
|
||||
TraceID: traceId,
|
||||
Instance: instance,
|
||||
Logs: logs,
|
||||
Total: len(logs),
|
||||
})
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "render error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// traceLogsJSON returns JSON for trace logs.
|
||||
func (rt *Router) traceLogsJSON(c *gin.Context) {
|
||||
traceId := ginx.UrlParamStr(c, "traceid")
|
||||
if !loggrep.IsValidTraceID(traceId) {
|
||||
ginx.Bomb(200, "invalid trace id format")
|
||||
}
|
||||
|
||||
logs, instance, err := rt.getTraceLogs(traceId)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
ginx.NewRender(c).Data(loggrep.EventDetailResp{
|
||||
Logs: logs,
|
||||
Instance: instance,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// getTraceLogs finds the same-engine instances and queries each one
|
||||
// until trace logs are found. Trace logs belong to a single instance.
|
||||
func (rt *Router) getTraceLogs(traceId string) ([]string, string, error) {
|
||||
keyword := "trace_id=" + traceId
|
||||
instance := fmt.Sprintf("%s:%d", rt.Alert.Heartbeat.IP, rt.HTTP.Port)
|
||||
engineName := rt.Alert.Heartbeat.EngineName
|
||||
|
||||
// try local first
|
||||
logs, err := loggrep.GrepLatestLogFiles(rt.LogDir, keyword)
|
||||
if err == nil && len(logs) > 0 {
|
||||
return logs, instance, nil
|
||||
}
|
||||
|
||||
// find all instances with the same engineName
|
||||
servers, err := models.AlertingEngineGetsInstances(rt.Ctx,
|
||||
"engine_cluster = ? and clock > ?",
|
||||
engineName, time.Now().Unix()-30)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// loop through remote instances until we find logs
|
||||
for _, node := range servers {
|
||||
if node == instance {
|
||||
continue // already tried local
|
||||
}
|
||||
|
||||
logs, nodeAddr, err := rt.forwardTraceLogs(node, traceId)
|
||||
if err != nil {
|
||||
logger.Errorf("forwardTraceLogs failed: %v", err)
|
||||
continue
|
||||
}
|
||||
if len(logs) > 0 {
|
||||
return logs, nodeAddr, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, instance, nil
|
||||
}
|
||||
|
||||
func (rt *Router) forwardTraceLogs(node, traceId string) ([]string, string, error) {
|
||||
url := fmt.Sprintf("http://%s/v1/n9e/trace-logs/%s", node, traceId)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, node, err
|
||||
}
|
||||
|
||||
for user, pass := range rt.HTTP.APIForService.BasicAuth {
|
||||
req.SetBasicAuth(user, pass)
|
||||
break
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 15 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, node, fmt.Errorf("forward to %s failed: %v", node, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 10*1024*1024))
|
||||
if err != nil {
|
||||
return nil, node, err
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Dat loggrep.EventDetailResp `json:"dat"`
|
||||
Err string `json:"err"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, node, err
|
||||
}
|
||||
if result.Err != "" {
|
||||
return nil, node, fmt.Errorf("%s", result.Err)
|
||||
}
|
||||
|
||||
return result.Dat.Logs, result.Dat.Instance, nil
|
||||
}
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/flashduty"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ormx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/secu"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/flashduty"
|
||||
"github.com/ccfos/nightingale/v6/pkg/strx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) userVariableConfigGets(context *gin.Context) {
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -178,14 +180,9 @@ func (c *Clickhouse) QueryData(ctx context.Context, query interface{}) ([]models
|
||||
|
||||
rows, err := c.QueryTimeseries(ctx, ckQueryParam)
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", ckQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", ckQueryParam, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", ckQueryParam, err)
|
||||
return []models.DataResp{}, err
|
||||
}
|
||||
data := make([]models.DataResp, 0)
|
||||
for i := range rows {
|
||||
data = append(data, models.DataResp{
|
||||
@@ -214,7 +211,7 @@ func (c *Clickhouse) QueryLog(ctx context.Context, query interface{}) ([]interfa
|
||||
|
||||
rows, err := c.Query(ctx, ckQueryParam)
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", ckQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", ckQueryParam, err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/ccfos/nightingale/v6/memsto"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
)
|
||||
|
||||
type FixedField string
|
||||
@@ -543,7 +544,7 @@ func QueryData(ctx context.Context, queryParam interface{}, cliTimeout int64, ve
|
||||
|
||||
source, _ := queryString.Source()
|
||||
b, _ := json.Marshal(source)
|
||||
logger.Debugf("query_data q:%+v indexArr:%+v tsAggr:%+v query_string:%s", param, indexArr, tsAggr, string(b))
|
||||
logx.Debugf(ctx, "query_data q:%+v indexArr:%+v tsAggr:%+v query_string:%s", param, indexArr, tsAggr, string(b))
|
||||
|
||||
searchSource := elastic.NewSearchSource().
|
||||
Query(queryString).
|
||||
@@ -551,29 +552,29 @@ func QueryData(ctx context.Context, queryParam interface{}, cliTimeout int64, ve
|
||||
|
||||
searchSourceString, err := searchSource.Source()
|
||||
if err != nil {
|
||||
logger.Warningf("query_data searchSource:%s to string error:%v", searchSourceString, err)
|
||||
logx.Warningf(ctx, "query_data searchSource:%s to string error:%v", searchSourceString, err)
|
||||
}
|
||||
|
||||
jsonSearchSource, err := json.Marshal(searchSourceString)
|
||||
if err != nil {
|
||||
logger.Warningf("query_data searchSource:%s to json error:%v", searchSourceString, err)
|
||||
logx.Warningf(ctx, "query_data searchSource:%s to json error:%v", searchSourceString, err)
|
||||
}
|
||||
|
||||
result, err := search(ctx, indexArr, searchSource, param.Timeout, param.MaxShard)
|
||||
if err != nil {
|
||||
logger.Warningf("query_data searchSource:%s query_data error:%v", searchSourceString, err)
|
||||
logx.Warningf(ctx, "query_data searchSource:%s query_data error:%v", searchSourceString, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查是否有 shard failures,有部分数据时仅记录警告继续处理
|
||||
if shardErr := checkShardFailures(result.Shards, "query_data", searchSourceString); shardErr != nil {
|
||||
if shardErr := checkShardFailures(ctx, result.Shards, "query_data", searchSourceString); shardErr != nil {
|
||||
if len(result.Aggregations["ts"]) == 0 {
|
||||
return nil, shardErr
|
||||
}
|
||||
// 有部分数据,checkShardFailures 已记录警告,继续处理
|
||||
}
|
||||
|
||||
logger.Debugf("query_data searchSource:%s resp:%s", string(jsonSearchSource), string(result.Aggregations["ts"]))
|
||||
logx.Infof(ctx, "query_data searchSource:%s resp:%s", string(jsonSearchSource), string(result.Aggregations["ts"]))
|
||||
|
||||
js, err := simplejson.NewJson(result.Aggregations["ts"])
|
||||
if err != nil {
|
||||
@@ -611,7 +612,7 @@ func QueryData(ctx context.Context, queryParam interface{}, cliTimeout int64, ve
|
||||
}
|
||||
|
||||
// checkShardFailures 检查 ES 查询结果中的 shard failures,返回格式化的错误信息
|
||||
func checkShardFailures(shards *elastic.ShardsInfo, logPrefix string, queryContext interface{}) error {
|
||||
func checkShardFailures(ctx context.Context, shards *elastic.ShardsInfo, logPrefix string, queryContext interface{}) error {
|
||||
if shards == nil || shards.Failed == 0 || len(shards.Failures) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -638,7 +639,7 @@ func checkShardFailures(shards *elastic.ShardsInfo, logPrefix string, queryConte
|
||||
|
||||
if len(failureReasons) > 0 {
|
||||
errMsg := fmt.Sprintf("elasticsearch shard failures (%d/%d failed): %s", shards.Failed, shards.Total, strings.Join(failureReasons, "; "))
|
||||
logger.Warningf("%s query:%v %s", logPrefix, queryContext, errMsg)
|
||||
logx.Warningf(ctx, "%s query:%v %s", logPrefix, queryContext, errMsg)
|
||||
return fmt.Errorf("%s", errMsg)
|
||||
}
|
||||
return nil
|
||||
@@ -723,12 +724,12 @@ func QueryLog(ctx context.Context, queryParam interface{}, timeout int64, versio
|
||||
sourceBytes, _ := json.Marshal(source)
|
||||
result, err := search(ctx, indexArr, source, param.Timeout, param.MaxShard)
|
||||
if err != nil {
|
||||
logger.Warningf("query_log source:%s error:%v", string(sourceBytes), err)
|
||||
logx.Warningf(ctx, "query_log source:%s error:%v", string(sourceBytes), err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 检查是否有 shard failures,有部分数据时仅记录警告继续处理
|
||||
if shardErr := checkShardFailures(result.Shards, "query_log", string(sourceBytes)); shardErr != nil {
|
||||
if shardErr := checkShardFailures(ctx, result.Shards, "query_log", string(sourceBytes)); shardErr != nil {
|
||||
if len(result.Hits.Hits) == 0 {
|
||||
return nil, 0, shardErr
|
||||
}
|
||||
@@ -737,17 +738,17 @@ func QueryLog(ctx context.Context, queryParam interface{}, timeout int64, versio
|
||||
|
||||
total := result.TotalHits()
|
||||
var ret []interface{}
|
||||
logger.Debugf("query_log source:%s len:%d total:%d", string(sourceBytes), len(result.Hits.Hits), total)
|
||||
logx.Debugf(ctx, "query_log source:%s len:%d total:%d", string(sourceBytes), len(result.Hits.Hits), total)
|
||||
|
||||
resultBytes, _ := json.Marshal(result)
|
||||
logger.Debugf("query_log source:%s result:%s", string(sourceBytes), string(resultBytes))
|
||||
logx.Debugf(ctx, "query_log source:%s result:%s", string(sourceBytes), string(resultBytes))
|
||||
|
||||
if strings.HasPrefix(version, "6") {
|
||||
for i := 0; i < len(result.Hits.Hits); i++ {
|
||||
var x map[string]interface{}
|
||||
err := json.Unmarshal(result.Hits.Hits[i].Source, &x)
|
||||
if err != nil {
|
||||
logger.Warningf("Unmarshal source error:%v", err)
|
||||
logx.Warningf(ctx, "Unmarshal source error:%v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -148,7 +150,7 @@ func (d *Doris) QueryData(ctx context.Context, query interface{}) ([]models.Data
|
||||
}
|
||||
}
|
||||
|
||||
items, err := d.QueryTimeseries(context.TODO(), &doris.QueryParam{
|
||||
items, err := d.QueryTimeseries(ctx, &doris.QueryParam{
|
||||
Database: dorisQueryParam.Database,
|
||||
Sql: dorisQueryParam.SQL,
|
||||
Keys: types.Keys{
|
||||
@@ -159,7 +161,7 @@ func (d *Doris) QueryData(ctx context.Context, query interface{}) ([]models.Data
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", dorisQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", dorisQueryParam, err)
|
||||
return []models.DataResp{}, err
|
||||
}
|
||||
data := make([]models.DataResp, 0)
|
||||
@@ -172,7 +174,7 @@ func (d *Doris) QueryData(ctx context.Context, query interface{}) ([]models.Data
|
||||
}
|
||||
|
||||
// parse resp to time series data
|
||||
logger.Infof("req:%+v keys:%+v \n data:%v", dorisQueryParam, dorisQueryParam.Keys, data)
|
||||
logx.Infof(ctx, "req:%+v keys:%+v \n data:%v", dorisQueryParam, dorisQueryParam.Keys, data)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -208,7 +210,7 @@ func (d *Doris) QueryLog(ctx context.Context, query interface{}) ([]interface{},
|
||||
Sql: dorisQueryParam.SQL,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", dorisQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", dorisQueryParam, err)
|
||||
return []interface{}{}, 0, err
|
||||
}
|
||||
logs := make([]interface{}, 0)
|
||||
|
||||
@@ -19,7 +19,8 @@ import (
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/olivere/elastic/v7"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -380,14 +381,14 @@ func (e *Elasticsearch) QueryMapData(ctx context.Context, query interface{}) ([]
|
||||
|
||||
var result []map[string]string
|
||||
for _, item := range res {
|
||||
logger.Debugf("query:%v item:%v", query, item)
|
||||
logx.Debugf(ctx, "query:%v item:%v", query, item)
|
||||
if itemMap, ok := item.(*elastic.SearchHit); ok {
|
||||
mItem := make(map[string]string)
|
||||
// 遍历 fields 字段的每个键值对
|
||||
sourceMap := make(map[string]interface{})
|
||||
err := json.Unmarshal(itemMap.Source, &sourceMap)
|
||||
if err != nil {
|
||||
logger.Warningf("unmarshal source%s error:%v", string(itemMap.Source), err)
|
||||
logx.Warningf(ctx, "unmarshal source%s error:%v", string(itemMap.Source), err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -165,7 +167,7 @@ func (m *MySQL) QueryData(ctx context.Context, query interface{}) ([]models.Data
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", mysqlQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", mysqlQueryParam, err)
|
||||
return []models.DataResp{}, err
|
||||
}
|
||||
data := make([]models.DataResp, 0)
|
||||
@@ -207,7 +209,7 @@ func (m *MySQL) QueryLog(ctx context.Context, query interface{}) ([]interface{},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", mysqlQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", mysqlQueryParam, err)
|
||||
return []interface{}{}, 0, err
|
||||
}
|
||||
logs := make([]interface{}, 0)
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -197,7 +199,7 @@ func (p *PostgreSQL) QueryData(ctx context.Context, query interface{}) ([]models
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", postgresqlQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", postgresqlQueryParam, err)
|
||||
return []models.DataResp{}, err
|
||||
}
|
||||
data := make([]models.DataResp, 0)
|
||||
@@ -210,7 +212,7 @@ func (p *PostgreSQL) QueryData(ctx context.Context, query interface{}) ([]models
|
||||
}
|
||||
|
||||
// parse resp to time series data
|
||||
logger.Infof("req:%+v keys:%+v \n data:%v", postgresqlQueryParam, postgresqlQueryParam.Keys, data)
|
||||
logx.Infof(ctx, "req:%+v keys:%+v \n data:%v", postgresqlQueryParam, postgresqlQueryParam.Keys, data)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -249,7 +251,7 @@ func (p *PostgreSQL) QueryLog(ctx context.Context, query interface{}) ([]interfa
|
||||
Sql: postgresqlQueryParam.SQL,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warningf("query:%+v get data err:%v", postgresqlQueryParam, err)
|
||||
logx.Warningf(ctx, "query:%+v get data err:%v", postgresqlQueryParam, err)
|
||||
return []interface{}{}, 0, err
|
||||
}
|
||||
logs := make([]interface{}, 0)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/datasource"
|
||||
td "github.com/ccfos/nightingale/v6/dskit/tdengine"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
@@ -118,7 +120,7 @@ func (td *TDengine) MakeTSQuery(ctx context.Context, query interface{}, eventTag
|
||||
}
|
||||
|
||||
func (td *TDengine) QueryData(ctx context.Context, queryParam interface{}) ([]models.DataResp, error) {
|
||||
return td.Query(queryParam, 0)
|
||||
return td.Query(ctx, queryParam, 0)
|
||||
}
|
||||
|
||||
func (td *TDengine) QueryLog(ctx context.Context, queryParam interface{}) ([]interface{}, int64, error) {
|
||||
@@ -170,7 +172,7 @@ func (td *TDengine) QueryMapData(ctx context.Context, query interface{}) ([]map[
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (td *TDengine) Query(query interface{}, delay ...int) ([]models.DataResp, error) {
|
||||
func (td *TDengine) Query(ctx context.Context, query interface{}, delay ...int) ([]models.DataResp, error) {
|
||||
b, err := json.Marshal(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -212,7 +214,7 @@ func (td *TDengine) Query(query interface{}, delay ...int) ([]models.DataResp, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debugf("tdengine query:%s result: %+v", q.Query, data)
|
||||
logx.Debugf(ctx, "tdengine query:%s result: %+v", q.Query, data)
|
||||
|
||||
return ConvertToTStData(data, q.Keys, q.Ref)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/pkg/poster"
|
||||
"github.com/ccfos/nightingale/v6/pkg/tplx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/unit"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ormx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/poster"
|
||||
"github.com/ccfos/nightingale/v6/pkg/secu"
|
||||
@@ -601,14 +602,14 @@ func incrLoginFailCount(ctx *ctx.Context, redisObj storage.Redis, username strin
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warningf("login_fail_count: failed to get redis value. key:%s, error:%s", key, err)
|
||||
logx.Warningf(ctx.Ctx, "login_fail_count: failed to get redis value. key:%s, error:%s", key, err)
|
||||
redisObj.Set(ctx.GetContext(), key, "1", duration)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
logger.Warningf("login_fail_count: failed to parse int64. key:%s, error:%s", key, err)
|
||||
logx.Warningf(ctx.Ctx, "login_fail_count: failed to parse int64. key:%s, error:%s", key, err)
|
||||
redisObj.Set(ctx.GetContext(), key, "1", duration)
|
||||
return
|
||||
}
|
||||
@@ -633,18 +634,18 @@ func PassLogin(ctx *ctx.Context, redis storage.Redis, username, pass string) (*U
|
||||
if needCheck {
|
||||
pair := strings.Fields(val)
|
||||
if len(pair) != 2 {
|
||||
logger.Warningf("login_fail_count config invalid: %s", val)
|
||||
logx.Warningf(ctx.Ctx, "login_fail_count config invalid: %s", val)
|
||||
needCheck = false
|
||||
} else {
|
||||
seconds, err = strconv.ParseInt(pair[0], 10, 64)
|
||||
if err != nil {
|
||||
logger.Warningf("login_fail_count seconds invalid: %s", pair[0])
|
||||
logx.Warningf(ctx.Ctx, "login_fail_count seconds invalid: %s", pair[0])
|
||||
needCheck = false
|
||||
}
|
||||
|
||||
count, err = strconv.ParseInt(pair[1], 10, 64)
|
||||
if err != nil {
|
||||
logger.Warningf("login_fail_count count invalid: %s", pair[1])
|
||||
logx.Warningf(ctx.Ctx, "login_fail_count count invalid: %s", pair[1])
|
||||
needCheck = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,12 +320,20 @@ func LoggerWithConfig(conf LoggerConfig) gin.HandlerFunc {
|
||||
|
||||
param.Path = path
|
||||
|
||||
// fmt.Fprint(out, formatter(param))
|
||||
logger.Info(formatter(param))
|
||||
traceId := c.GetString("trace_id")
|
||||
if traceId != "" {
|
||||
logger.Infof("trace_id=%s %s", traceId, formatter(param))
|
||||
} else {
|
||||
logger.Info(formatter(param))
|
||||
}
|
||||
if conf.ContainsPath(c.Request.RequestURI) {
|
||||
respBody := readBody(bytes.NewReader(bodyWriter.body.Bytes()), c.Writer.Header().Get("Content-Encoding"))
|
||||
reqBody := readBody(rdr1, c.Request.Header.Get("Content-Encoding"))
|
||||
logger.Debugf("path:%s req body:%s resp:%s", path, reqBody, respBody)
|
||||
if traceId != "" {
|
||||
logger.Debugf("trace_id=%s path:%s req body:%s resp:%s", traceId, path, reqBody, respBody)
|
||||
} else {
|
||||
logger.Debugf("path:%s req body:%s resp:%s", path, reqBody, respBody)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,10 @@ func RecoveryWithWriter(out io.Writer) gin.HandlerFunc {
|
||||
if e.Code != 200 {
|
||||
c.String(e.Code, i18n.Sprintf(c.GetHeader("X-Language"), e.Message))
|
||||
} else {
|
||||
c.JSON(e.Code, gin.H{"err": i18n.Sprintf(c.GetHeader("X-Language"), e.Message)})
|
||||
c.JSON(e.Code, gin.H{
|
||||
"err": i18n.Sprintf(c.GetHeader("X-Language"), e.Message),
|
||||
"request_id": c.GetString("trace_id"),
|
||||
})
|
||||
}
|
||||
c.Abort()
|
||||
return
|
||||
|
||||
@@ -43,3 +43,14 @@ func (c *Context) GetContext() context.Context {
|
||||
func (c *Context) GetDB() *gorm.DB {
|
||||
return c.DB
|
||||
}
|
||||
|
||||
// WithContext returns a shallow copy with a different standard context.
|
||||
// Useful for carrying per-request values (e.g. traceId) without mutating the global instance.
|
||||
func (c *Context) WithContext(stdCtx context.Context) *Context {
|
||||
return &Context{
|
||||
DB: c.DB,
|
||||
CenterApi: c.CenterApi,
|
||||
Ctx: stdCtx,
|
||||
IsCenter: c.IsCenter,
|
||||
}
|
||||
}
|
||||
|
||||
11
pkg/ginx/errorx.go
Normal file
11
pkg/ginx/errorx.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package ginx
|
||||
|
||||
import "github.com/toolkits/pkg/errorx"
|
||||
|
||||
func Bomb(code int, format string, a ...interface{}) {
|
||||
errorx.Bomb(code, format, a...)
|
||||
}
|
||||
|
||||
func Dangerous(v interface{}, code ...int) {
|
||||
errorx.Dangerous(v, code...)
|
||||
}
|
||||
19
pkg/ginx/funcs.go
Normal file
19
pkg/ginx/funcs.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package ginx
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Offset(c *gin.Context, limit int, pagenoVarName ...string) int {
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
pageno := "p"
|
||||
if len(pagenoVarName) > 0 {
|
||||
pageno = pagenoVarName[0]
|
||||
}
|
||||
|
||||
page := QueryInt(c, pageno, 1)
|
||||
return (page - 1) * limit
|
||||
}
|
||||
106
pkg/ginx/param.go
Normal file
106
pkg/ginx/param.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package ginx
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/errorx"
|
||||
)
|
||||
|
||||
func BindJSON(c *gin.Context, ptr interface{}) {
|
||||
err := c.ShouldBindJSON(ptr)
|
||||
if err != nil {
|
||||
errorx.Bomb(http.StatusBadRequest, "json body invalid: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func UrlParamStr(c *gin.Context, field string) string {
|
||||
val := c.Param(field)
|
||||
|
||||
if val == "" {
|
||||
errorx.Bomb(http.StatusBadRequest, "url param[%s] is blank", field)
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func UrlParamInt64(c *gin.Context, field string) int64 {
|
||||
strval := UrlParamStr(c, field)
|
||||
intval, err := strconv.ParseInt(strval, 10, 64)
|
||||
if err != nil {
|
||||
errorx.Bomb(http.StatusBadRequest, "cannot convert %s to int64", strval)
|
||||
}
|
||||
|
||||
return intval
|
||||
}
|
||||
|
||||
func UrlParamInt(c *gin.Context, field string) int {
|
||||
return int(UrlParamInt64(c, field))
|
||||
}
|
||||
|
||||
func QueryStr(c *gin.Context, key string, defaultVal ...string) string {
|
||||
val := c.Query(key)
|
||||
if val != "" {
|
||||
return val
|
||||
}
|
||||
|
||||
if len(defaultVal) == 0 {
|
||||
errorx.Bomb(http.StatusBadRequest, "query param[%s] is necessary", key)
|
||||
}
|
||||
|
||||
return defaultVal[0]
|
||||
}
|
||||
|
||||
func QueryInt(c *gin.Context, key string, defaultVal ...int) int {
|
||||
strv := c.Query(key)
|
||||
if strv != "" {
|
||||
intv, err := strconv.Atoi(strv)
|
||||
if err != nil {
|
||||
errorx.Bomb(http.StatusBadRequest, "cannot convert [%s] to int", strv)
|
||||
}
|
||||
return intv
|
||||
}
|
||||
|
||||
if len(defaultVal) == 0 {
|
||||
errorx.Bomb(http.StatusBadRequest, "query param[%s] is necessary", key)
|
||||
}
|
||||
|
||||
return defaultVal[0]
|
||||
}
|
||||
|
||||
func QueryInt64(c *gin.Context, key string, defaultVal ...int64) int64 {
|
||||
strv := c.Query(key)
|
||||
if strv != "" {
|
||||
intv, err := strconv.ParseInt(strv, 10, 64)
|
||||
if err != nil {
|
||||
errorx.Bomb(http.StatusBadRequest, "cannot convert [%s] to int64", strv)
|
||||
}
|
||||
return intv
|
||||
}
|
||||
|
||||
if len(defaultVal) == 0 {
|
||||
errorx.Bomb(http.StatusBadRequest, "query param[%s] is necessary", key)
|
||||
}
|
||||
|
||||
return defaultVal[0]
|
||||
}
|
||||
|
||||
func QueryBool(c *gin.Context, key string, defaultVal ...bool) bool {
|
||||
strv := c.Query(key)
|
||||
if strv != "" {
|
||||
if strv == "true" || strv == "1" || strv == "on" || strv == "checked" || strv == "yes" || strv == "Y" {
|
||||
return true
|
||||
} else if strv == "false" || strv == "0" || strv == "off" || strv == "no" || strv == "N" {
|
||||
return false
|
||||
} else {
|
||||
errorx.Bomb(http.StatusBadRequest, "unknown arg[%s] value: %s", key, strv)
|
||||
}
|
||||
}
|
||||
|
||||
if len(defaultVal) == 0 {
|
||||
errorx.Bomb(http.StatusBadRequest, "arg[%s] is necessary", key)
|
||||
}
|
||||
|
||||
return defaultVal[0]
|
||||
}
|
||||
66
pkg/ginx/render.go
Normal file
66
pkg/ginx/render.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package ginx
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/i18n"
|
||||
)
|
||||
|
||||
type Render struct {
|
||||
code int
|
||||
ctx *gin.Context
|
||||
}
|
||||
|
||||
func NewRender(c *gin.Context, code ...int) Render {
|
||||
r := Render{ctx: c}
|
||||
if len(code) > 0 {
|
||||
r.code = code[0]
|
||||
} else {
|
||||
r.code = 200
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r Render) Message(v interface{}, a ...interface{}) {
|
||||
requestId := r.ctx.GetString("trace_id")
|
||||
if v == nil {
|
||||
if r.code == 200 {
|
||||
r.ctx.JSON(r.code, gin.H{"err": "", "request_id": requestId})
|
||||
} else {
|
||||
r.ctx.String(r.code, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
msg := i18n.Sprintf(r.ctx.GetHeader("X-Language"), t, a...)
|
||||
if r.code == 200 {
|
||||
r.ctx.JSON(r.code, gin.H{"err": msg, "request_id": requestId})
|
||||
} else {
|
||||
r.ctx.String(r.code, msg)
|
||||
}
|
||||
case error:
|
||||
msg := i18n.Sprintf(r.ctx.GetHeader("X-Language"), t.Error(), a...)
|
||||
if r.code == 200 {
|
||||
r.ctx.JSON(r.code, gin.H{"err": msg, "request_id": requestId})
|
||||
} else {
|
||||
r.ctx.String(r.code, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r Render) Data(data interface{}, err interface{}, a ...interface{}) {
|
||||
if err == nil {
|
||||
r.ctx.JSON(r.code, gin.H{"dat": data, "err": "", "request_id": r.ctx.GetString("trace_id")})
|
||||
return
|
||||
}
|
||||
|
||||
r.Message(err, a...)
|
||||
}
|
||||
|
||||
func (r Render) ZeroPage() {
|
||||
r.Data(gin.H{
|
||||
"list": []int{},
|
||||
"total": 0,
|
||||
}, nil)
|
||||
}
|
||||
@@ -10,10 +10,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/aop"
|
||||
"github.com/ccfos/nightingale/v6/pkg/logx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/version"
|
||||
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
@@ -91,6 +93,8 @@ func GinEngine(mode string, cfg Config, printBodyPaths func() map[string]struct{
|
||||
|
||||
r := gin.New()
|
||||
|
||||
r.Use(traceIdMid())
|
||||
|
||||
r.Use(recoveryMid)
|
||||
|
||||
r.Use(loggerMid)
|
||||
@@ -126,6 +130,32 @@ func GinEngine(mode string, cfg Config, printBodyPaths func() map[string]struct{
|
||||
return r
|
||||
}
|
||||
|
||||
func traceIdMid() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.GetHeader("X-Trace-Id")
|
||||
if !isValidTraceId(id) {
|
||||
id = uuid.New().String()
|
||||
}
|
||||
c.Set("trace_id", id)
|
||||
ctx := logx.NewTraceContext(c.Request.Context(), id)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Header("X-Trace-Id", id)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func isValidTraceId(id string) bool {
|
||||
if id == "" || len(id) > 64 {
|
||||
return false
|
||||
}
|
||||
for _, r := range id {
|
||||
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' || r == '_') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Init(cfg Config, handler http.Handler) func() {
|
||||
addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
|
||||
srv := &http.Server{
|
||||
|
||||
@@ -15,6 +15,7 @@ const MaxLogLines = 5000
|
||||
|
||||
var hashPattern = regexp.MustCompile(`^[a-f0-9]{32,64}$`)
|
||||
var idPattern = regexp.MustCompile(`^[1-9]\d*$`)
|
||||
var traceIdPattern = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,64}$`)
|
||||
|
||||
// IsValidHash checks whether s looks like a valid MD5/SHA hex hash.
|
||||
func IsValidHash(s string) bool {
|
||||
@@ -26,6 +27,11 @@ func IsValidRuleID(s string) bool {
|
||||
return idPattern.MatchString(s)
|
||||
}
|
||||
|
||||
// IsValidTraceID checks whether s looks like a valid trace ID (alphanumeric, hyphens, underscores).
|
||||
func IsValidTraceID(s string) bool {
|
||||
return traceIdPattern.MatchString(s)
|
||||
}
|
||||
|
||||
type EventDetailResp struct {
|
||||
Logs []string `json:"logs"`
|
||||
Instance string `json:"instance"`
|
||||
@@ -45,6 +51,13 @@ type AlertEvalPageData struct {
|
||||
Total int
|
||||
}
|
||||
|
||||
type TraceLogsPageData struct {
|
||||
TraceID string
|
||||
Instance string
|
||||
Logs []string
|
||||
Total int
|
||||
}
|
||||
|
||||
// GrepLogDir searches all log files in logDir for lines containing keyword,
|
||||
// sorts them by timestamp descending, and truncates to MaxLogLines.
|
||||
func GrepLogDir(logDir string, keyword string) ([]string, error) {
|
||||
@@ -73,6 +86,34 @@ func GrepLogDir(logDir string, keyword string) ([]string, error) {
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// GrepLatestLogFiles searches only the current (non-rotated) log files in logDir
|
||||
// (i.e. files matching *.log without any additional suffix like .log.20240101).
|
||||
func GrepLatestLogFiles(logDir string, keyword string) ([]string, error) {
|
||||
logFiles, err := filepath.Glob(filepath.Join(logDir, "*.log"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var logs []string
|
||||
for _, logFile := range logFiles {
|
||||
lines, err := GrepFile(logFile, keyword)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
logs = append(logs, lines...)
|
||||
}
|
||||
|
||||
sort.Slice(logs, func(i, j int) bool {
|
||||
return logs[i] > logs[j]
|
||||
})
|
||||
|
||||
if len(logs) > MaxLogLines {
|
||||
logs = logs[:MaxLogLines]
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// GrepFile scans a file line by line and returns lines containing the keyword.
|
||||
func GrepFile(filePath string, keyword string) ([]string, error) {
|
||||
f, err := os.Open(filePath)
|
||||
@@ -103,6 +144,11 @@ func RenderAlertEvalHTML(w io.Writer, data AlertEvalPageData) error {
|
||||
return alertEvalHtmlTpl.Execute(w, data)
|
||||
}
|
||||
|
||||
// RenderTraceLogsHTML writes the trace logs HTML page to w.
|
||||
func RenderTraceLogsHTML(w io.Writer, data TraceLogsPageData) error {
|
||||
return traceLogsHtmlTpl.Execute(w, data)
|
||||
}
|
||||
|
||||
var htmlTpl = template.Must(template.New("event-detail").Parse(`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -303,6 +349,196 @@ var htmlTpl = template.Must(template.New("event-detail").Parse(`<!DOCTYPE html>
|
||||
</html>
|
||||
`))
|
||||
|
||||
var traceLogsHtmlTpl = template.Must(template.New("trace-logs").Parse(`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Trace Logs - {{.TraceID}}</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: #f0f2f5; color: #333;
|
||||
}
|
||||
.header {
|
||||
background: #fff; border-bottom: 1px solid #ebebeb;
|
||||
padding: 16px 24px; position: sticky; top: 0; z-index: 10;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
||||
}
|
||||
.header-top { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; }
|
||||
.header h1 { font-size: 18px; font-weight: 600; color: #333; }
|
||||
.header h1 span { color: #6C53B1; font-family: "SFMono-Regular", Consolas, monospace; font-size: 15px; }
|
||||
.badges { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
.badge {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 2px 10px; border-radius: 12px; font-size: 12px; font-weight: 500;
|
||||
}
|
||||
.badge-instance { background: #f0ecf7; color: #6C53B1; }
|
||||
.badge-count { background: #f5f5f5; color: #666; }
|
||||
.toolbar {
|
||||
margin-top: 12px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
||||
}
|
||||
.search-box {
|
||||
flex: 1; min-width: 200px; position: relative;
|
||||
}
|
||||
.search-box input {
|
||||
width: 100%; padding: 6px 12px 6px 32px;
|
||||
background: #fff; border: 1px solid #d9d9d9; border-radius: 6px;
|
||||
color: #333; font-size: 13px; outline: none;
|
||||
}
|
||||
.search-box input:focus { border-color: #6C53B1; box-shadow: 0 0 0 2px rgba(108,83,177,0.2); }
|
||||
.search-box svg {
|
||||
position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
|
||||
width: 16px; height: 16px; fill: #bfbfbf;
|
||||
}
|
||||
.filter-btns button {
|
||||
padding: 4px 12px; border-radius: 6px; border: 1px solid #d9d9d9;
|
||||
background: #fff; color: #666; font-size: 12px; cursor: pointer;
|
||||
}
|
||||
.filter-btns button:hover { border-color: #6C53B1; color: #6C53B1; }
|
||||
.filter-btns button.active { background: #f0ecf7; border-color: #6C53B1; color: #6C53B1; }
|
||||
.log-container { padding: 8px 0; background: #fff; margin: 12px; border-radius: 8px; border: 1px solid #ebebeb; }
|
||||
.log-line {
|
||||
display: flex; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
font-size: 12px; line-height: 20px; padding: 0 24px;
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
.log-line:hover { background: #fafafa; border-color: #ebebeb; }
|
||||
.line-no {
|
||||
min-width: 48px; text-align: right; color: #bfbfbf;
|
||||
padding-right: 16px; user-select: none; flex-shrink: 0;
|
||||
}
|
||||
.line-content { white-space: pre-wrap; word-break: break-all; flex: 1; color: #333; }
|
||||
.line-content .ts { color: #1890ff; }
|
||||
.line-content .lv-DEBUG { color: #8c8c8c; }
|
||||
.line-content .lv-INFO { color: #1890ff; }
|
||||
.line-content .lv-WARNING { color: #fa8c16; }
|
||||
.line-content .lv-WARNINGF { color: #fa8c16; }
|
||||
.line-content .lv-ERROR { color: #f5222d; }
|
||||
.line-content .lv-ERRORF { color: #f5222d; }
|
||||
.line-content .hl { background: #fff7e6; color: #d46b08; border-radius: 2px; padding: 0 2px; }
|
||||
.hidden { display: none !important; }
|
||||
.empty-state {
|
||||
text-align: center; padding: 64px 24px; color: #bfbfbf; font-size: 14px;
|
||||
}
|
||||
.match-count { font-size: 12px; color: #999; white-space: nowrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="header-top">
|
||||
<h1>Trace Logs — <span>{{.TraceID}}</span></h1>
|
||||
<div class="badges">
|
||||
<span class="badge badge-instance">⚙ {{.Instance}}</span>
|
||||
<span class="badge badge-count" id="countBadge">{{.Total}} lines</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar">
|
||||
<div class="search-box">
|
||||
<svg viewBox="0 0 16 16"><path d="M11.5 7a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm-.82 4.74a6 6 0 1 1 1.06-1.06l3.04 3.04a.75.75 0 1 1-1.06 1.06l-3.04-3.04Z"/></svg>
|
||||
<input type="text" id="searchInput" placeholder="Filter logs..." autocomplete="off">
|
||||
</div>
|
||||
<div class="filter-btns" id="levelBtns">
|
||||
<button data-level="all" class="active">All</button>
|
||||
<button data-level="ERROR">Error</button>
|
||||
<button data-level="WARNING">Warn</button>
|
||||
<button data-level="INFO">Info</button>
|
||||
<button data-level="DEBUG">Debug</button>
|
||||
</div>
|
||||
<span class="match-count" id="matchCount"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-container" id="logContainer">
|
||||
{{- if eq .Total 0}}
|
||||
<div class="empty-state">No log lines found for trace ID {{.TraceID}}.</div>
|
||||
{{- else}}
|
||||
{{- range $i, $line := .Logs}}
|
||||
<div class="log-line" data-idx="{{$i}}"><span class="line-no">{{$i}}</span><span class="line-content">{{$line}}</span></div>
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var keyword = "trace_id=" + {{.TraceID}};
|
||||
var lines = document.querySelectorAll('.log-line');
|
||||
var searchInput = document.getElementById('searchInput');
|
||||
var levelBtns = document.getElementById('levelBtns').querySelectorAll('button');
|
||||
var matchCount = document.getElementById('matchCount');
|
||||
var countBadge = document.getElementById('countBadge');
|
||||
var activeLevel = 'all';
|
||||
var LEVELS = ['DEBUG','INFO','WARNING','WARNINGF','ERROR','ERRORF'];
|
||||
var LEVEL_RE = /\b(DEBUG|INFO|WARNING|WARNINGF|ERROR|ERRORF)\b/;
|
||||
var TS_RE = /^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}[.\d]*)/;
|
||||
|
||||
lines.forEach(function(el) {
|
||||
var content = el.querySelector('.line-content');
|
||||
var text = content.textContent;
|
||||
var html = escapeHtml(text);
|
||||
html = html.replace(TS_RE, '<span class="ts">$1</span>');
|
||||
html = html.replace(LEVEL_RE, function(m) { return '<span class="lv-'+m+'">'+m+'</span>'; });
|
||||
if (keyword) {
|
||||
html = html.split(escapeHtml(keyword)).join('<span class="hl">'+escapeHtml(keyword)+'</span>');
|
||||
}
|
||||
content.innerHTML = html;
|
||||
el.dataset.level = detectLevel(text);
|
||||
});
|
||||
|
||||
var debounceTimer;
|
||||
searchInput.addEventListener('input', function() {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(applyFilters, 150);
|
||||
});
|
||||
|
||||
levelBtns.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
levelBtns.forEach(function(b) { b.classList.remove('active'); });
|
||||
btn.classList.add('active');
|
||||
activeLevel = btn.dataset.level;
|
||||
applyFilters();
|
||||
});
|
||||
});
|
||||
|
||||
function applyFilters() {
|
||||
var q = searchInput.value.toLowerCase();
|
||||
var visible = 0;
|
||||
lines.forEach(function(el) {
|
||||
var text = el.querySelector('.line-content').textContent.toLowerCase();
|
||||
var levelOk = activeLevel === 'all' || matchLevel(el.dataset.level, activeLevel);
|
||||
var searchOk = !q || text.indexOf(q) !== -1;
|
||||
if (levelOk && searchOk) {
|
||||
el.classList.remove('hidden');
|
||||
visible++;
|
||||
} else {
|
||||
el.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
matchCount.textContent = q || activeLevel !== 'all' ? visible + ' / ' + lines.length + ' shown' : '';
|
||||
}
|
||||
|
||||
function matchLevel(lineLevel, filter) {
|
||||
if (filter === 'ERROR') return lineLevel === 'ERROR' || lineLevel === 'ERRORF';
|
||||
if (filter === 'WARNING') return lineLevel === 'WARNING' || lineLevel === 'WARNINGF';
|
||||
return lineLevel === filter;
|
||||
}
|
||||
|
||||
function detectLevel(text) {
|
||||
var m = text.match(LEVEL_RE);
|
||||
return m ? m[1] : '';
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
var d = document.createElement('div');
|
||||
d.appendChild(document.createTextNode(s));
|
||||
return d.innerHTML;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
|
||||
var alertEvalHtmlTpl = template.Must(template.New("alert-eval-detail").Parse(`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -46,3 +47,44 @@ func Init(c Config) (func(), error) {
|
||||
logger.Close()
|
||||
}, nil
|
||||
}
|
||||
|
||||
// traceKey is the context key for storing traceId.
|
||||
type traceKey struct{}
|
||||
|
||||
// NewTraceContext returns a new context carrying the given traceId.
|
||||
func NewTraceContext(ctx context.Context, traceId string) context.Context {
|
||||
return context.WithValue(ctx, traceKey{}, traceId)
|
||||
}
|
||||
|
||||
// GetTraceId extracts the traceId from ctx, or returns "" if absent.
|
||||
func GetTraceId(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
return ""
|
||||
}
|
||||
id, _ := ctx.Value(traceKey{}).(string)
|
||||
return id
|
||||
}
|
||||
|
||||
func prefix(ctx context.Context) string {
|
||||
id := GetTraceId(ctx)
|
||||
if id == "" {
|
||||
return ""
|
||||
}
|
||||
return "trace_id=" + id + " "
|
||||
}
|
||||
|
||||
func Infof(ctx context.Context, format string, args ...interface{}) {
|
||||
logger.Infof(prefix(ctx)+format, args...)
|
||||
}
|
||||
|
||||
func Errorf(ctx context.Context, format string, args ...interface{}) {
|
||||
logger.Errorf(prefix(ctx)+format, args...)
|
||||
}
|
||||
|
||||
func Warningf(ctx context.Context, format string, args ...interface{}) {
|
||||
logger.Warningf(prefix(ctx)+format, args...)
|
||||
}
|
||||
|
||||
func Debugf(ctx context.Context, format string, args ...interface{}) {
|
||||
logger.Debugf(prefix(ctx)+format, args...)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/center/metas"
|
||||
"github.com/ccfos/nightingale/v6/memsto"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/httpx"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/idents"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/pconf"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/pstat"
|
||||
"github.com/ccfos/nightingale/v6/pushgw/writer"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
)
|
||||
|
||||
type HandleTSFunc func(pt *prompb.TimeSeries) *prompb.TimeSeries
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/center/metas"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/poster"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pushgw/pstat"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/golang/snappy"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ package router
|
||||
|
||||
import (
|
||||
"github.com/ccfos/nightingale/v6/pushgw/idents"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/toolkits/pkg/ginx"
|
||||
)
|
||||
|
||||
func (rt *Router) targetUpdate(c *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user