diff --git a/LICENSE b/LICENSE index 2a87eb49bc81ff2b652b4c5dfa6ce612eca2dc05..cee01710d9efc897bbf9f02a2295d5fa3ac8bb68 100644 --- a/LICENSE +++ b/LICENSE @@ -430,4 +430,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and -limitations under the License. +limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile index 44ee9792d5c5c1c5b0069933dd0eb52601814a2a..1f16daa300d58d5ed92715e362c227aa79ba97f7 100644 --- a/Makefile +++ b/Makefile @@ -1,50 +1,30 @@ .PHONY: start build -NOW = $(shell date -u '+%Y%m%d%I%M%S') - - -APP = n9e -SERVER_BIN = $(APP) ROOT:=$(shell pwd -P) GIT_COMMIT:=$(shell git --work-tree ${ROOT} rev-parse 'HEAD^{commit}') _GIT_VERSION:=$(shell git --work-tree ${ROOT} describe --tags --abbrev=14 "${GIT_COMMIT}^{commit}" 2>/dev/null) TAG=$(shell echo "${_GIT_VERSION}" | awk -F"-" '{print $$1}') RELEASE_VERSION:="$(TAG)-$(GIT_COMMIT)" -# RELEASE_ROOT = release -# RELEASE_SERVER = release/${APP} -# GIT_COUNT = $(shell git rev-list --all --count) -# GIT_HASH = $(shell git rev-parse --short HEAD) -# RELEASE_TAG = $(RELEASE_VERSION).$(GIT_COUNT).$(GIT_HASH) - all: build build: - go build -ldflags "-w -s -X github.com/didi/nightingale/v5/src/pkg/version.VERSION=$(RELEASE_VERSION)" -o $(SERVER_BIN) ./src - -build-linux: - GOOS=linux GOARCH=amd64 go build -ldflags "-w -s -X github.com/didi/nightingale/v5/src/pkg/version.VERSION=$(RELEASE_VERSION)" -o $(SERVER_BIN) ./src - -# start: -# @go run -ldflags "-X main.VERSION=$(RELEASE_TAG)" ./cmd/${APP}/main.go web -c ./configs/config.toml -m ./configs/model.conf --menu ./configs/menu.yaml -run_webapi: - nohup ./n9e webapi > webapi.log 2>&1 & + go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e ./cmd/center/main.go -run_server: - nohup ./n9e server > server.log 2>&1 & +build-alert: + go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-alert ./cmd/alert/main.go -# swagger: -# @swag init --parseDependency --generalInfo ./cmd/${APP}/main.go --output ./internal/app/swagger +build-pushgw: + go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-pushgw ./cmd/pushgw/main.go -# wire: -# @wire gen ./internal/app +build-cli: + go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-cli ./cmd/cli/main.go -# test: -# cd ./internal/app/test && go test -v +run: + nohup ./n9e > n9e.log 2>&1 & -# clean: -# rm -rf data release $(SERVER_BIN) internal/app/test/data cmd/${APP}/data +run_alert: + nohup ./n9e-alert > n9e-alert.log 2>&1 & -pack: build - rm -rf $(APP)-$(RELEASE_VERSION).tar.gz - tar -zcvf $(APP)-$(RELEASE_VERSION).tar.gz docker etc $(SERVER_BIN) pub/font pub/index.html pub/assets pub/image +run_pushgw: + nohup ./n9e-pushgw > n9e-pushgw.log 2>&1 & \ No newline at end of file diff --git a/README.md b/README.md index a222d3a21f79eb116f80e93e41f69d23de81a19e..01daaeb4d001905caded06b2dd0c38bbbd74d555 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ - 补充和完善文档 => [n9e.github.io](https://n9e.github.io/) - 分享您在使用夜莺监控过程中的最佳实践和经验心得 => [文章分享](https://n9e.github.io/docs/prologue/share/) - 提交产品建议 =》 [github issue](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Ffeature&template=enhancement.md) -- 提交代码,让夜莺监控更快、更稳、更好用 => [github pull request](https://github.com/didi/nightingale/pulls) +- 提交代码,让夜莺监控更快、更稳、更好用 => [github pull request](https://github.com/ccfos/nightingale/pulls) **尊重、认可和记录每一位贡献者的工作**是夜莺开源社区的第一指导原则,我们提倡**高效的提问**,这既是对开发者时间的尊重,也是对整个社区知识沉淀的贡献: - 提问之前请先查阅 [FAQ](https://www.gitlink.org.cn/ccfos/nightingale/wiki/faq) @@ -112,7 +112,7 @@ ## License -[Apache License V2.0](https://github.com/didi/nightingale/blob/main/LICENSE) +[Apache License V2.0](https://github.com/ccfos/nightingale/blob/main/LICENSE) ## Contact Us 推荐您关注夜莺监控公众号,及时获取相关产品和社区动态: diff --git a/README_EN.md b/README_EN.md index 00357e2532d71f2bc7630bb83aed52cc836d6b2c..dd6ef7f31be16db5841632e76747e1b85ab3d26d 100644 --- a/README_EN.md +++ b/README_EN.md @@ -49,7 +49,7 @@ Nightingale is an cloud-native monitoring system by All-In-On design, support en ## Contact us and feedback questions -- We recommend that you use [github issue](https://github.com/didi/nightingale/issues) as the preferred channel for issue feedback and requirement submission; +- We recommend that you use [github issue](https://github.com/ccfos/nightingale/issues) as the preferred channel for issue feedback and requirement submission; - You can join our WeChat group @@ -57,12 +57,12 @@ Nightingale is an cloud-native monitoring system by All-In-On design, support en ## Contributing We welcome your participation in the Nightingale open source project and open source community in a variety of ways: -- Feedback on problems and bugs => [github issue](https://github.com/didi/nightingale/issues) +- Feedback on problems and bugs => [github issue](https://github.com/ccfos/nightingale/issues) - Additional and improved documentation => [n9e.github.io](https://n9e.github.io/) -- Share your best practices and insights on using Nightingale => [User Story](https://github.com/didi/nightingale/issues/897) -- Join our community events => [Nightingale wechat group](https://s3-gz01.didistatic.com/n9e-pub/image/n9e-wx.png) -- Submit code to make Nightingale better =>[github PR](https://github.com/didi/nightingale/pulls) +- Share your best practices and insights on using Nightingale => [User Story](https://github.com/ccfos/nightingale/issues/897) +- Join our community events => [Nightingale wechat group](https://s3-gz01.ccfosstatic.com/n9e-pub/image/n9e-wx.png) +- Submit code to make Nightingale better =>[github PR](https://github.com/ccfos/nightingale/pulls) ## License -Nightingale with [Apache License V2.0](https://github.com/didi/nightingale/blob/main/LICENSE) open source license. +Nightingale with [Apache License V2.0](https://github.com/ccfos/nightingale/blob/main/LICENSE) open source license. diff --git a/alert/aconf/conf.go b/alert/aconf/conf.go new file mode 100644 index 0000000000000000000000000000000000000000..68fcde54e0927bd6c4b5085c79b757ac6644f80a --- /dev/null +++ b/alert/aconf/conf.go @@ -0,0 +1,71 @@ +package aconf + +import ( + "path" + + "github.com/toolkits/pkg/runner" +) + +type Alert struct { + EngineDelay int64 + Heartbeat HeartbeatConfig + Alerting Alerting + SMTP SMTPConfig + Ibex Ibex +} + +type SMTPConfig struct { + Host string + Port int + User string + Pass string + From string + InsecureSkipVerify bool + Batch int +} + +type HeartbeatConfig struct { + IP string + Interval int64 + Endpoint string + ClusterName string +} + +type Alerting struct { + Timeout int64 + TemplatesDir string + NotifyConcurrency int +} + +type CallPlugin struct { + Enable bool + PluginPath string + Caller string +} + +type RedisPub struct { + Enable bool + ChannelPrefix string + ChannelKey string +} + +type Ibex struct { + Address string + BasicAuthUser string + BasicAuthPass string + Timeout int64 +} + +func (a *Alert) PreCheck() { + if a.Alerting.TemplatesDir == "" { + a.Alerting.TemplatesDir = path.Join(runner.Cwd, "etc", "template") + } + + if a.Alerting.Timeout == 0 { + a.Alerting.Timeout = 30000 + } + + if a.Heartbeat.Interval == 0 { + a.Heartbeat.Interval = 1000 + } +} diff --git a/alert/alert.go b/alert/alert.go new file mode 100644 index 0000000000000000000000000000000000000000..34a1e9dc50fabde39d67af129a604a1a60192a8f --- /dev/null +++ b/alert/alert.go @@ -0,0 +1,99 @@ +package alert + +import ( + "context" + "fmt" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/ccfos/nightingale/v6/alert/dispatch" + "github.com/ccfos/nightingale/v6/alert/eval" + "github.com/ccfos/nightingale/v6/alert/naming" + "github.com/ccfos/nightingale/v6/alert/process" + "github.com/ccfos/nightingale/v6/alert/queue" + "github.com/ccfos/nightingale/v6/alert/record" + "github.com/ccfos/nightingale/v6/alert/router" + "github.com/ccfos/nightingale/v6/alert/sender" + "github.com/ccfos/nightingale/v6/conf" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/httpx" + "github.com/ccfos/nightingale/v6/pkg/logx" + "github.com/ccfos/nightingale/v6/prom" + "github.com/ccfos/nightingale/v6/pushgw/pconf" + "github.com/ccfos/nightingale/v6/pushgw/writer" + "github.com/ccfos/nightingale/v6/storage" +) + +func Initialize(configDir string, cryptoKey string) (func(), error) { + config, err := conf.InitConfig(configDir, cryptoKey) + if err != nil { + return nil, fmt.Errorf("failed to init config: %v", err) + } + + logxClean, err := logx.Init(config.Log) + if err != nil { + return nil, err + } + + db, err := storage.New(config.DB) + if err != nil { + return nil, err + } + ctx := ctx.NewContext(context.Background(), db) + + syncStats := memsto.NewSyncStats() + alertStats := astats.NewSyncStats() + + targetCache := memsto.NewTargetCache(ctx, syncStats) + busiGroupCache := memsto.NewBusiGroupCache(ctx, syncStats) + alertMuteCache := memsto.NewAlertMuteCache(ctx, syncStats) + + alertRuleCache := memsto.NewAlertRuleCache(ctx, syncStats) + + promClients := prom.NewPromClient(ctx, config.Alert.Heartbeat) + + externalProcessors := process.NewExternalProcessors() + + Start(config.Alert, config.Pushgw, syncStats, alertStats, externalProcessors, targetCache, busiGroupCache, alertMuteCache, alertRuleCache, ctx, promClients) + + r := httpx.GinEngine(config.Global.RunMode, config.HTTP) + rt := router.New(config.HTTP, config.Alert, alertMuteCache, targetCache, busiGroupCache, alertStats, ctx, externalProcessors) + rt.Config(r) + + httpClean := httpx.Init(config.HTTP, r) + + return func() { + logxClean() + httpClean() + }, nil +} + +func Start(alertc aconf.Alert, pushgwc pconf.Pushgw, syncStats *memsto.Stats, alertStats *astats.Stats, externalProcessors *process.ExternalProcessorsType, targetCache *memsto.TargetCacheType, busiGroupCache *memsto.BusiGroupCacheType, + alertMuteCache *memsto.AlertMuteCacheType, alertRuleCache *memsto.AlertRuleCacheType, ctx *ctx.Context, promClients *prom.PromClientMap) { + userCache := memsto.NewUserCache(ctx, syncStats) + userGroupCache := memsto.NewUserGroupCache(ctx, syncStats) + alertSubscribeCache := memsto.NewAlertSubscribeCache(ctx, syncStats) + recordingRuleCache := memsto.NewRecordingRuleCache(ctx, syncStats) + webhookCache := memsto.NewWebhookCache(ctx) + notifyScript := memsto.NewNotifyScript(ctx) + + go models.InitNotifyConfig(ctx, alertc.Alerting.TemplatesDir) + + naming := naming.NewNaming(ctx, alertc.Heartbeat) + + writers := writer.NewWriters(pushgwc) + record.NewScheduler(alertc, recordingRuleCache, promClients, writers, alertStats) + + eval.NewScheduler(alertc, externalProcessors, alertRuleCache, targetCache, busiGroupCache, alertMuteCache, promClients, naming, ctx, alertStats) + + dp := dispatch.NewDispatch(alertRuleCache, userCache, userGroupCache, alertSubscribeCache, targetCache, webhookCache, notifyScript, alertc.Alerting, alertc.Ibex, ctx) + consumer := dispatch.NewConsumer(alertc.Alerting, ctx, dp) + + go dp.ReloadTpls() + go consumer.LoopConsume() + + go queue.ReportQueueSize(alertStats) + go sender.StartEmailSender(alertc.SMTP) +} diff --git a/src/server/stat/stat.go b/alert/astats/stats.go similarity index 61% rename from src/server/stat/stat.go rename to alert/astats/stats.go index ca870315c901446c54284a0fe1bb9ed693765c25..e7519fa57a9b09e29c637aecb239f1b721b6068c 100644 --- a/src/server/stat/stat.go +++ b/alert/astats/stats.go @@ -1,4 +1,4 @@ -package stat +package astats import ( "github.com/prometheus/client_golang/prometheus" @@ -6,28 +6,21 @@ import ( const ( namespace = "n9e" - subsystem = "server" + subsystem = "alert" ) -var ( - // 各个周期性任务的执行耗时 - GaugeCronDuration = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: subsystem, - Name: "cron_duration", - Help: "Cron method use duration, unit: ms.", - }, []string{"name"}) - - // 从数据库同步数据的时候,同步的条数 - GaugeSyncNumber = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: subsystem, - Name: "cron_sync_number", - Help: "Cron sync number.", - }, []string{"name"}) +type Stats struct { + CounterSampleTotal *prometheus.CounterVec + CounterAlertsTotal *prometheus.CounterVec + GaugeAlertQueueSize prometheus.Gauge + GaugeSampleQueueSize *prometheus.GaugeVec + RequestDuration *prometheus.HistogramVec + ForwardDuration *prometheus.HistogramVec +} +func NewSyncStats() *Stats { // 从各个接收接口接收到的监控数据总量 - CounterSampleTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ + CounterSampleTotal := prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, Name: "samples_received_total", @@ -35,7 +28,7 @@ var ( }, []string{"cluster", "channel"}) // 产生的告警总量 - CounterAlertsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ + CounterAlertsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, Name: "alerts_total", @@ -43,7 +36,7 @@ var ( }, []string{"cluster"}) // 内存中的告警事件队列的长度 - GaugeAlertQueueSize = prometheus.NewGauge(prometheus.GaugeOpts{ + GaugeAlertQueueSize := prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "alert_queue_size", @@ -51,7 +44,7 @@ var ( }) // 数据转发队列,各个队列的长度 - GaugeSampleQueueSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + GaugeSampleQueueSize := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "sample_queue_size", @@ -59,7 +52,7 @@ var ( }, []string{"cluster", "channel_number"}) // 一些重要的请求,比如接收数据的请求,应该统计一下延迟情况 - RequestDuration = prometheus.NewHistogramVec( + RequestDuration := prometheus.NewHistogramVec( prometheus.HistogramOpts{ Namespace: namespace, Subsystem: subsystem, @@ -70,7 +63,7 @@ var ( ) // 发往后端TSDB,延迟如何 - ForwardDuration = prometheus.NewHistogramVec( + ForwardDuration := prometheus.NewHistogramVec( prometheus.HistogramOpts{ Namespace: namespace, Subsystem: subsystem, @@ -79,13 +72,8 @@ var ( Help: "Forward samples to TSDB. latencies in seconds.", }, []string{"cluster", "channel_number"}, ) -) -func Init() { - // Register the summary and the histogram with Prometheus's default registry. prometheus.MustRegister( - GaugeCronDuration, - GaugeSyncNumber, CounterSampleTotal, CounterAlertsTotal, GaugeAlertQueueSize, @@ -93,4 +81,13 @@ func Init() { RequestDuration, ForwardDuration, ) + + return &Stats{ + CounterSampleTotal: CounterSampleTotal, + CounterAlertsTotal: CounterAlertsTotal, + GaugeAlertQueueSize: GaugeAlertQueueSize, + GaugeSampleQueueSize: GaugeSampleQueueSize, + RequestDuration: RequestDuration, + ForwardDuration: ForwardDuration, + } } diff --git a/src/server/common/conv/conv.go b/alert/common/conv.go similarity index 65% rename from src/server/common/conv/conv.go rename to alert/common/conv.go index 0e38d378b3a4fa27e34c033565f6a8ceb2cc0cc3..bf1c069764ed49854efcf849b99e86b1be95dde4 100644 --- a/src/server/common/conv/conv.go +++ b/alert/common/conv.go @@ -1,4 +1,4 @@ -package conv +package common import ( "fmt" @@ -8,20 +8,36 @@ import ( "github.com/prometheus/common/model" ) -type Vector struct { +type AnomalyPoint struct { Key string `json:"key"` Labels model.Metric `json:"labels"` Timestamp int64 `json:"timestamp"` Value float64 `json:"value"` + Severity int `json:"severity"` } -func (v *Vector) ReadableValue() string { +func NewAnomalyPoint(key string, labels map[string]string, ts int64, value float64, severity int) AnomalyPoint { + anomalyPointLabels := make(model.Metric) + for k, v := range labels { + anomalyPointLabels[model.LabelName(k)] = model.LabelValue(v) + } + anomalyPointLabels[model.MetricNameLabel] = model.LabelValue(key) + return AnomalyPoint{ + Key: key, + Labels: anomalyPointLabels, + Timestamp: ts, + Value: value, + Severity: severity, + } +} + +func (v *AnomalyPoint) ReadableValue() string { ret := fmt.Sprintf("%.5f", v.Value) ret = strings.TrimRight(ret, "0") return strings.TrimRight(ret, ".") } -func ConvertVectors(value model.Value) (lst []Vector) { +func ConvertAnomalyPoints(value model.Value) (lst []AnomalyPoint) { if value == nil { return } @@ -38,7 +54,7 @@ func ConvertVectors(value model.Value) (lst []Vector) { continue } - lst = append(lst, Vector{ + lst = append(lst, AnomalyPoint{ Key: item.Metric.String(), Timestamp: item.Timestamp.Unix(), Value: float64(item.Value), @@ -62,7 +78,7 @@ func ConvertVectors(value model.Value) (lst []Vector) { continue } - lst = append(lst, Vector{ + lst = append(lst, AnomalyPoint{ Key: item.Metric.String(), Labels: item.Metric, Timestamp: last.Timestamp.Unix(), @@ -79,7 +95,7 @@ func ConvertVectors(value model.Value) (lst []Vector) { return } - lst = append(lst, Vector{ + lst = append(lst, AnomalyPoint{ Key: "{}", Timestamp: item.Timestamp.Unix(), Value: float64(item.Value), diff --git a/alert/common/key.go b/alert/common/key.go new file mode 100644 index 0000000000000000000000000000000000000000..0458e95b8ae77468fa45502e9eb21680b7ffddf5 --- /dev/null +++ b/alert/common/key.go @@ -0,0 +1,45 @@ +package common + +import ( + "fmt" + + "github.com/ccfos/nightingale/v6/models" +) + +func RuleKey(datasourceId, id int64) string { + return fmt.Sprintf("alert-%d-%d", datasourceId, id) +} + +func MatchTags(eventTagsMap map[string]string, itags []models.TagFilter) bool { + for _, filter := range itags { + value, has := eventTagsMap[filter.Key] + if !has { + return false + } + if !matchTag(value, filter) { + return false + } + } + return true +} + +func matchTag(value string, filter models.TagFilter) bool { + switch filter.Func { + case "==": + return filter.Value == value + case "!=": + return filter.Value != value + case "in": + _, has := filter.Vset[value] + return has + case "not in": + _, has := filter.Vset[value] + return !has + case "=~": + return filter.Regexp.MatchString(value) + case "!~": + return !filter.Regexp.MatchString(value) + } + // unexpect func + return false +} diff --git a/src/server/engine/consume.go b/alert/dispatch/consume.go similarity index 60% rename from src/server/engine/consume.go rename to alert/dispatch/consume.go index 480d4bbd41d2619f511e89f0624595dc7a77d187..0b4e2ab7c25d417ed613c2d1391ab762b2d6ef95 100644 --- a/src/server/engine/consume.go +++ b/alert/dispatch/consume.go @@ -1,33 +1,48 @@ -package engine +package dispatch import ( - "context" "fmt" - "strconv" "time" + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/alert/queue" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/toolkits/pkg/concurrent/semaphore" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" ) -func loopConsume(ctx context.Context) { - sema := semaphore.NewSemaphore(config.C.Alerting.NotifyConcurrency) +type Consumer struct { + alerting aconf.Alerting + ctx *ctx.Context + + dispatch *Dispatch +} + +// 创建一个 Consumer 实例 +func NewConsumer(alerting aconf.Alerting, ctx *ctx.Context, dispatch *Dispatch) *Consumer { + return &Consumer{ + alerting: alerting, + ctx: ctx, + dispatch: dispatch, + } +} + +func (e *Consumer) LoopConsume() { + sema := semaphore.NewSemaphore(e.alerting.NotifyConcurrency) duration := time.Duration(100) * time.Millisecond for { - events := EventQueue.PopBackBy(100) + events := queue.EventQueue.PopBackBy(100) if len(events) == 0 { time.Sleep(duration) continue } - consume(events, sema) + e.consume(events, sema) } } -func consume(events []interface{}, sema *semaphore.Semaphore) { +func (e *Consumer) consume(events []interface{}, sema *semaphore.Semaphore) { for i := range events { if events[i] == nil { continue @@ -37,12 +52,12 @@ func consume(events []interface{}, sema *semaphore.Semaphore) { sema.Acquire() go func(event *models.AlertCurEvent) { defer sema.Release() - consumeOne(event) + e.consumeOne(event) }(event) } } -func consumeOne(event *models.AlertCurEvent) { +func (e *Consumer) consumeOne(event *models.AlertCurEvent) { LogEvent(event, "consume") if err := event.ParseRule("rule_name"); err != nil { @@ -53,26 +68,32 @@ func consumeOne(event *models.AlertCurEvent) { event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err) } - persist(event) + if err := event.ParseRule("annotations"); err != nil { + event.Annotations = fmt.Sprintf("failed to parse rule note: %v", err) + } + + logger.Info("event_persist: event.Annotations", event.Annotations) + + e.persist(event) if event.IsRecovered && event.NotifyRecovered == 0 { return } - HandleEventNotify(event, false) + e.dispatch.HandleEventNotify(event, false) } -func persist(event *models.AlertCurEvent) { - has, err := models.AlertCurEventExists("hash=?", event.Hash) +func (e *Consumer) persist(event *models.AlertCurEvent) { + has, err := models.AlertCurEventExists(e.ctx, "hash=?", event.Hash) if err != nil { logger.Errorf("event_persist_check_exists_fail: %v rule_id=%d hash=%s", err, event.RuleId, event.Hash) return } - his := event.ToHis() + his := event.ToHis(e.ctx) // 不管是告警还是恢复,全量告警里都要记录 - if err := his.Add(); err != nil { + if err := his.Add(e.ctx); err != nil { logger.Errorf( "event_persist_his_fail: %v rule_id=%d cluster:%s hash=%s tags=%v timestamp=%d value=%s", err, @@ -87,7 +108,7 @@ func persist(event *models.AlertCurEvent) { if has { // 活跃告警表中有记录,删之 - err = models.AlertCurEventDelByHash(event.Hash) + err = models.AlertCurEventDelByHash(e.ctx, event.Hash) if err != nil { logger.Errorf("event_del_cur_fail: %v hash=%s", err, event.Hash) return @@ -98,7 +119,7 @@ func persist(event *models.AlertCurEvent) { // use his id as cur id event.Id = his.Id if event.Id > 0 { - if err := event.Add(); err != nil { + if err := event.Add(e.ctx); err != nil { logger.Errorf( "event_persist_cur_fail: %v rule_id=%d cluster:%s hash=%s tags=%v timestamp=%d value=%s", err, @@ -124,7 +145,7 @@ func persist(event *models.AlertCurEvent) { // use his id as cur id event.Id = his.Id if event.Id > 0 { - if err := event.Add(); err != nil { + if err := event.Add(e.ctx); err != nil { logger.Errorf( "event_persist_cur_fail: %v rule_id=%d cluster:%s hash=%s tags=%v timestamp=%d value=%s", err, @@ -138,35 +159,3 @@ func persist(event *models.AlertCurEvent) { } } } - -// for alerting -func fillUsers(e *models.AlertCurEvent) { - gids := make([]int64, 0, len(e.NotifyGroupsJSON)) - for i := 0; i < len(e.NotifyGroupsJSON); i++ { - gid, err := strconv.ParseInt(e.NotifyGroupsJSON[i], 10, 64) - if err != nil { - continue - } - gids = append(gids, gid) - } - - e.NotifyGroupsObj = memsto.UserGroupCache.GetByUserGroupIds(gids) - - uids := make(map[int64]struct{}) - for i := 0; i < len(e.NotifyGroupsObj); i++ { - ug := e.NotifyGroupsObj[i] - for j := 0; j < len(ug.UserIds); j++ { - uids[ug.UserIds[j]] = struct{}{} - } - } - - e.NotifyUsersObj = memsto.UserCache.GetByUserIds(mapKeys(uids)) -} - -func mapKeys(m map[int64]struct{}) []int64 { - lst := make([]int64, 0, len(m)) - for k := range m { - lst = append(lst, k) - } - return lst -} diff --git a/alert/dispatch/dispatch.go b/alert/dispatch/dispatch.go new file mode 100644 index 0000000000000000000000000000000000000000..cde14aaabec72eb7c81877f55cb72486c845891a --- /dev/null +++ b/alert/dispatch/dispatch.go @@ -0,0 +1,262 @@ +package dispatch + +import ( + "bytes" + "encoding/json" + "html/template" + "strconv" + "sync" + "time" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/alert/common" + "github.com/ccfos/nightingale/v6/alert/sender" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + + "github.com/toolkits/pkg/logger" +) + +type Dispatch struct { + alertRuleCache *memsto.AlertRuleCacheType + userCache *memsto.UserCacheType + userGroupCache *memsto.UserGroupCacheType + alertSubscribeCache *memsto.AlertSubscribeCacheType + targetCache *memsto.TargetCacheType + webhookCache *memsto.WebhookCacheType + notifyScriptCache *memsto.NotifyScriptCacheType + + alerting aconf.Alerting + ibex aconf.Ibex + + senders map[string]sender.Sender + tpls map[string]*template.Template + + ctx *ctx.Context + + RwLock sync.RWMutex +} + +// 创建一个 Notify 实例 +func NewDispatch(alertRuleCache *memsto.AlertRuleCacheType, userCache *memsto.UserCacheType, userGroupCache *memsto.UserGroupCacheType, + alertSubscribeCache *memsto.AlertSubscribeCacheType, targetCache *memsto.TargetCacheType, webhookCache *memsto.WebhookCacheType, notifyScriptCache *memsto.NotifyScriptCacheType, + alerting aconf.Alerting, ibex aconf.Ibex, ctx *ctx.Context) *Dispatch { + notify := &Dispatch{ + alertRuleCache: alertRuleCache, + userCache: userCache, + userGroupCache: userGroupCache, + alertSubscribeCache: alertSubscribeCache, + targetCache: targetCache, + webhookCache: webhookCache, + notifyScriptCache: notifyScriptCache, + + alerting: alerting, + ibex: ibex, + + senders: make(map[string]sender.Sender), + tpls: make(map[string]*template.Template), + + ctx: ctx, + } + return notify +} + +func (e *Dispatch) ReloadTpls() error { + err := e.relaodTpls() + if err != nil { + logger.Error("failed to reload tpls: %v", err) + } + + duration := time.Duration(9000) * time.Millisecond + for { + time.Sleep(duration) + if err := e.relaodTpls(); err != nil { + logger.Warning("failed to reload tpls:", err) + } + } +} + +func (e *Dispatch) relaodTpls() error { + tmpTpls, err := models.ListTpls(e.ctx) + if err != nil { + return err + } + + senders := map[string]sender.Sender{ + models.Email: sender.NewSender(models.Email, tmpTpls), + models.Dingtalk: sender.NewSender(models.Dingtalk, tmpTpls), + models.Wecom: sender.NewSender(models.Wecom, tmpTpls), + models.Feishu: sender.NewSender(models.Feishu, tmpTpls), + models.Mm: sender.NewSender(models.Mm, tmpTpls), + models.Telegram: sender.NewSender(models.Telegram, tmpTpls), + } + + e.RwLock.Lock() + e.tpls = tmpTpls + e.senders = senders + e.RwLock.Unlock() + return nil +} + +// HandleEventNotify 处理event事件的主逻辑 +// event: 告警/恢复事件 +// isSubscribe: 告警事件是否由subscribe的配置产生 +func (e *Dispatch) HandleEventNotify(event *models.AlertCurEvent, isSubscribe bool) { + rule := e.alertRuleCache.Get(event.RuleId) + if rule == nil { + return + } + fillUsers(event, e.userCache, e.userGroupCache) + + var ( + // 处理事件到 notifyTarget 关系,处理的notifyTarget用OrMerge进行合并 + handlers []NotifyTargetDispatch + + // 额外去掉一些订阅,处理的notifyTarget用AndMerge进行合并, 如设置 channel=false,合并后不通过这个channel发送 + // 如果实现了相关 Dispatch,可以添加到interceptors中 + interceptorHandlers []NotifyTargetDispatch + ) + if isSubscribe { + handlers = []NotifyTargetDispatch{NotifyGroupDispatch, EventCallbacksDispatch} + } else { + handlers = []NotifyTargetDispatch{NotifyGroupDispatch, GlobalWebhookDispatch, EventCallbacksDispatch} + } + + notifyTarget := NewNotifyTarget() + // 处理订阅关系使用OrMerge + for _, handler := range handlers { + notifyTarget.OrMerge(handler(rule, event, notifyTarget, e)) + } + + // 处理移除订阅关系的逻辑,比如员工离职,临时静默某个通道的策略等 + for _, handler := range interceptorHandlers { + notifyTarget.AndMerge(handler(rule, event, notifyTarget, e)) + } + + // 处理事件发送,这里用一个goroutine处理一个event的所有发送事件 + go e.Send(rule, event, notifyTarget, isSubscribe) + + // 如果是不是订阅规则出现的event, 则需要处理订阅规则的event + if !isSubscribe { + e.handleSubs(event) + } +} + +func (e *Dispatch) handleSubs(event *models.AlertCurEvent) { + // handle alert subscribes + subscribes := make([]*models.AlertSubscribe, 0) + // rule specific subscribes + if subs, has := e.alertSubscribeCache.Get(event.RuleId); has { + subscribes = append(subscribes, subs...) + } + // global subscribes + if subs, has := e.alertSubscribeCache.Get(0); has { + subscribes = append(subscribes, subs...) + } + + for _, sub := range subscribes { + e.handleSub(sub, *event) + } +} + +// handleSub 处理订阅规则的event,注意这里event要使用值传递,因为后面会修改event的状态 +func (e *Dispatch) handleSub(sub *models.AlertSubscribe, event models.AlertCurEvent) { + if sub.IsDisabled() || !sub.MatchCluster(event.DatasourceId) { + return + } + if !common.MatchTags(event.TagsMap, sub.ITags) { + return + } + if sub.ForDuration > (event.TriggerTime - event.FirstTriggerTime) { + return + } + sub.ModifyEvent(&event) + LogEvent(&event, "subscribe") + e.HandleEventNotify(&event, true) +} + +func (e *Dispatch) Send(rule *models.AlertRule, event *models.AlertCurEvent, notifyTarget *NotifyTarget, isSubscribe bool) { + for channel, uids := range notifyTarget.ToChannelUserMap() { + ctx := sender.BuildMessageContext(rule, event, uids, e.userCache) + e.RwLock.RLock() + s := e.senders[channel] + e.RwLock.RUnlock() + if s == nil { + logger.Warningf("no sender for channel: %s", channel) + continue + } + s.Send(ctx) + } + + // handle event callbacks + sender.SendCallbacks(e.ctx, notifyTarget.ToCallbackList(), event, e.targetCache, e.ibex) + + // handle global webhooks + sender.SendWebhooks(notifyTarget.ToWebhookList(), event) + + // handle plugin call + go sender.MayPluginNotify(e.genNoticeBytes(event), e.notifyScriptCache) +} + +type Notice struct { + Event *models.AlertCurEvent `json:"event"` + Tpls map[string]string `json:"tpls"` +} + +func (e *Dispatch) genNoticeBytes(event *models.AlertCurEvent) []byte { + // build notice body with templates + ntpls := make(map[string]string) + + e.RwLock.RLock() + defer e.RwLock.RUnlock() + for filename, tpl := range e.tpls { + var body bytes.Buffer + if err := tpl.Execute(&body, event); err != nil { + ntpls[filename] = err.Error() + } else { + ntpls[filename] = body.String() + } + } + + notice := Notice{Event: event, Tpls: ntpls} + stdinBytes, err := json.Marshal(notice) + if err != nil { + logger.Errorf("event_notify: failed to marshal notice: %v", err) + return nil + } + + return stdinBytes +} + +// for alerting +func fillUsers(ce *models.AlertCurEvent, uc *memsto.UserCacheType, ugc *memsto.UserGroupCacheType) { + gids := make([]int64, 0, len(ce.NotifyGroupsJSON)) + for i := 0; i < len(ce.NotifyGroupsJSON); i++ { + gid, err := strconv.ParseInt(ce.NotifyGroupsJSON[i], 10, 64) + if err != nil { + continue + } + gids = append(gids, gid) + } + + ce.NotifyGroupsObj = ugc.GetByUserGroupIds(gids) + + uids := make(map[int64]struct{}) + for i := 0; i < len(ce.NotifyGroupsObj); i++ { + ug := ce.NotifyGroupsObj[i] + for j := 0; j < len(ug.UserIds); j++ { + uids[ug.UserIds[j]] = struct{}{} + } + } + + ce.NotifyUsersObj = uc.GetByUserIds(mapKeys(uids)) +} + +func mapKeys(m map[int64]struct{}) []int64 { + lst := make([]int64, 0, len(m)) + for k := range m { + lst = append(lst, k) + } + return lst +} diff --git a/src/server/engine/logger.go b/alert/dispatch/log.go similarity index 89% rename from src/server/engine/logger.go rename to alert/dispatch/log.go index 190b868e024690910c1d91aa7e29d1da47a9cd8b..f51f7fb12b1d71372dca0d02b5fe9062edf6f990 100644 --- a/src/server/engine/logger.go +++ b/alert/dispatch/log.go @@ -1,9 +1,9 @@ -package engine +package dispatch import ( - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" - "github.com/didi/nightingale/v5/src/models" + "github.com/toolkits/pkg/logger" ) func LogEvent(event *models.AlertCurEvent, location string, err ...error) { diff --git a/alert/dispatch/notify_channel.go b/alert/dispatch/notify_channel.go new file mode 100644 index 0000000000000000000000000000000000000000..cb2fd8c00f6432806972b3347ba6d7d9ecc71a27 --- /dev/null +++ b/alert/dispatch/notify_channel.go @@ -0,0 +1,33 @@ +package dispatch + +// NotifyChannels channelKey -> bool +type NotifyChannels map[string]bool + +func NewNotifyChannels(channels []string) NotifyChannels { + nc := make(NotifyChannels) + for _, ch := range channels { + nc[ch] = true + } + return nc +} + +func (nc NotifyChannels) OrMerge(other NotifyChannels) { + nc.merge(other, func(a, b bool) bool { return a || b }) +} + +func (nc NotifyChannels) AndMerge(other NotifyChannels) { + nc.merge(other, func(a, b bool) bool { return a && b }) +} + +func (nc NotifyChannels) merge(other NotifyChannels, f func(bool, bool) bool) { + if other == nil { + return + } + for k, v := range other { + if curV, has := nc[k]; has { + nc[k] = f(curV, v) + } else { + nc[k] = v + } + } +} diff --git a/alert/dispatch/notify_target.go b/alert/dispatch/notify_target.go new file mode 100644 index 0000000000000000000000000000000000000000..453ecab94564a9296eacbb5045ad4afbe7af1e64 --- /dev/null +++ b/alert/dispatch/notify_target.go @@ -0,0 +1,134 @@ +package dispatch + +import ( + "strconv" + + "github.com/ccfos/nightingale/v6/models" +) + +// NotifyTarget 维护所有需要发送的目标 用户-通道/回调/钩子信息,用map维护的数据结构具有去重功能 +type NotifyTarget struct { + userMap map[int64]NotifyChannels + webhooks map[string]*models.Webhook + callbacks map[string]struct{} +} + +func NewNotifyTarget() *NotifyTarget { + return &NotifyTarget{ + userMap: make(map[int64]NotifyChannels), + webhooks: make(map[string]*models.Webhook), + callbacks: make(map[string]struct{}), + } +} + +// OrMerge 将 channelMap 按照 or 的方式合并,方便实现多种组合的策略,比如根据某个 tag 进行路由等 +func (s *NotifyTarget) OrMerge(other *NotifyTarget) { + s.merge(other, NotifyChannels.OrMerge) +} + +// AndMerge 将 channelMap 中的 bool 值按照 and 的逻辑进行合并,可以单独将人/通道维度的通知移除 +// 常用的场景有: +// 1. 人员离职了不需要发送告警了 +// 2. 某个告警通道进行维护,暂时不需要发送告警了 +// 3. 业务值班的重定向逻辑,将高等级的告警额外发送给应急人员等 +// 可以结合业务需求自己实现router +func (s *NotifyTarget) AndMerge(other *NotifyTarget) { + s.merge(other, NotifyChannels.AndMerge) +} + +func (s *NotifyTarget) merge(other *NotifyTarget, f func(NotifyChannels, NotifyChannels)) { + if other == nil { + return + } + for k, v := range other.userMap { + if curV, has := s.userMap[k]; has { + f(curV, v) + } else { + s.userMap[k] = v + } + } + for k, v := range other.webhooks { + s.webhooks[k] = v + } + for k, v := range other.callbacks { + s.callbacks[k] = v + } +} + +// ToChannelUserMap userMap(map[uid][channel]bool) 转换为 map[channel][]uid 的结构 +func (s *NotifyTarget) ToChannelUserMap() map[string][]int64 { + m := make(map[string][]int64) + for uid, nc := range s.userMap { + for ch, send := range nc { + if send { + m[ch] = append(m[ch], uid) + } + } + } + return m +} + +func (s *NotifyTarget) ToCallbackList() []string { + callbacks := make([]string, 0, len(s.callbacks)) + for cb := range s.callbacks { + callbacks = append(callbacks, cb) + } + return callbacks +} + +func (s *NotifyTarget) ToWebhookList() []*models.Webhook { + webhooks := make([]*models.Webhook, 0, len(s.webhooks)) + for _, wh := range s.webhooks { + webhooks = append(webhooks, wh) + } + return webhooks +} + +// Dispatch 抽象由告警事件到信息接收者的路由策略 +// rule: 告警规则 +// event: 告警事件 +// prev: 前一次路由结果, Dispatch 的实现可以直接修改 prev, 也可以返回一个新的 NotifyTarget 用于 AndMerge/OrMerge +type NotifyTargetDispatch func(rule *models.AlertRule, event *models.AlertCurEvent, prev *NotifyTarget, dispatch *Dispatch) *NotifyTarget + +// GroupDispatch 处理告警规则的组订阅关系 +func NotifyGroupDispatch(rule *models.AlertRule, event *models.AlertCurEvent, prev *NotifyTarget, dispatch *Dispatch) *NotifyTarget { + groupIds := make([]int64, 0, len(event.NotifyGroupsJSON)) + for _, groupId := range event.NotifyGroupsJSON { + gid, err := strconv.ParseInt(groupId, 10, 64) + if err != nil { + continue + } + groupIds = append(groupIds, gid) + } + + groups := dispatch.userGroupCache.GetByUserGroupIds(groupIds) + NotifyTarget := NewNotifyTarget() + for _, group := range groups { + for _, userId := range group.UserIds { + NotifyTarget.userMap[userId] = NewNotifyChannels(event.NotifyChannelsJSON) + } + } + return NotifyTarget +} + +func GlobalWebhookDispatch(rule *models.AlertRule, event *models.AlertCurEvent, prev *NotifyTarget, dispatch *Dispatch) *NotifyTarget { + webhooks := dispatch.webhookCache.GetWebhooks() + NotifyTarget := NewNotifyTarget() + for _, webhook := range webhooks { + if !webhook.Enable { + continue + } + NotifyTarget.webhooks[webhook.Url] = webhook + } + return NotifyTarget +} + +func EventCallbacksDispatch(rule *models.AlertRule, event *models.AlertCurEvent, prev *NotifyTarget, dispatch *Dispatch) *NotifyTarget { + for _, c := range event.CallbacksJSON { + if c == "" { + continue + } + prev.callbacks[c] = struct{}{} + } + return nil +} diff --git a/alert/eval/alert_rule.go b/alert/eval/alert_rule.go new file mode 100644 index 0000000000000000000000000000000000000000..20654d9e56bdec9e7ec7715441f04f3459ecec44 --- /dev/null +++ b/alert/eval/alert_rule.go @@ -0,0 +1,149 @@ +package eval + +import ( + "context" + "fmt" + "time" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/ccfos/nightingale/v6/alert/naming" + "github.com/ccfos/nightingale/v6/alert/process" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/prom" +) + +type Scheduler struct { + // key: hash + alertRules map[string]*AlertRuleWorker + + ExternalProcessors *process.ExternalProcessorsType + + aconf aconf.Alert + + alertRuleCache *memsto.AlertRuleCacheType + targetCache *memsto.TargetCacheType + busiGroupCache *memsto.BusiGroupCacheType + alertMuteCache *memsto.AlertMuteCacheType + + promClients *prom.PromClientMap + + naming *naming.Naming + + ctx *ctx.Context + stats *astats.Stats +} + +func NewScheduler(aconf aconf.Alert, externalProcessors *process.ExternalProcessorsType, arc *memsto.AlertRuleCacheType, targetCache *memsto.TargetCacheType, + busiGroupCache *memsto.BusiGroupCacheType, alertMuteCache *memsto.AlertMuteCacheType, promClients *prom.PromClientMap, naming *naming.Naming, + ctx *ctx.Context, stats *astats.Stats) *Scheduler { + scheduler := &Scheduler{ + aconf: aconf, + alertRules: make(map[string]*AlertRuleWorker), + // recordRules: make(map[string]RuleContext), + // externalAlertRules: make(map[string]*eval.AlertRuleWorker), + + ExternalProcessors: externalProcessors, + + alertRuleCache: arc, + targetCache: targetCache, + busiGroupCache: busiGroupCache, + alertMuteCache: alertMuteCache, + + promClients: promClients, + naming: naming, + + ctx: ctx, + stats: stats, + } + + go scheduler.LoopSyncRules(context.Background()) + return scheduler +} + +func (s *Scheduler) LoopSyncRules(ctx context.Context) { + time.Sleep(time.Duration(s.aconf.EngineDelay) * time.Second) + duration := 9000 * time.Millisecond + for { + select { + case <-ctx.Done(): + return + case <-time.After(duration): + s.syncAlertRules() + } + } +} + +func (s *Scheduler) syncAlertRules() { + ids := s.alertRuleCache.GetRuleIds() + alertRuleWorkers := make(map[string]*AlertRuleWorker) + externalRuleWorkers := make(map[string]*process.Processor) + for _, id := range ids { + rule := s.alertRuleCache.Get(id) + if rule == nil { + continue + } + if rule.IsPrometheusRule() { + datasourceIds := s.promClients.Hit(rule.DatasourceIdsJson) + for _, dsId := range datasourceIds { + if !naming.DatasourceHashRing.IsHit(dsId, fmt.Sprintf("%d", rule.Id), s.aconf.Heartbeat.Endpoint) { + continue + } + + processor := process.NewProcessor(rule, dsId, s.alertRuleCache, s.targetCache, s.busiGroupCache, s.alertMuteCache, s.promClients, s.ctx, s.stats) + + alertRule := NewAlertRuleWorker(rule, dsId, processor, s.promClients, s.ctx) + alertRuleWorkers[alertRule.Hash()] = alertRule + } + } else if rule.IsHostRule() && s.naming.IamLeader() { + // all host rule will be processed by leader + processor := process.NewProcessor(rule, 0, s.alertRuleCache, s.targetCache, s.busiGroupCache, s.alertMuteCache, s.promClients, s.ctx, s.stats) + alertRule := NewAlertRuleWorker(rule, 0, processor, s.promClients, s.ctx) + alertRuleWorkers[alertRule.Hash()] = alertRule + } else { + // 如果 rule 不是通过 prometheus engine 来告警的,则创建为 externalRule + // if rule is not processed by prometheus engine, create it as externalRule + for _, dsId := range rule.DatasourceIdsJson { + processor := process.NewProcessor(rule, dsId, s.alertRuleCache, s.targetCache, s.busiGroupCache, s.alertMuteCache, s.promClients, s.ctx, s.stats) + externalRuleWorkers[processor.Key()] = processor + } + } + } + + for hash, rule := range alertRuleWorkers { + if _, has := s.alertRules[hash]; !has { + rule.Prepare() + rule.Start() + s.alertRules[hash] = rule + } + } + + for hash, rule := range s.alertRules { + if _, has := alertRuleWorkers[hash]; !has { + rule.Stop() + delete(s.alertRules, hash) + } + } + + s.ExternalProcessors.ExternalLock.Lock() + for key, processor := range externalRuleWorkers { + if curProcessor, has := s.ExternalProcessors.Processors[key]; has { + // rule存在,且hash一致,认为没有变更,这里可以根据需求单独实现一个关联数据更多的hash函数 + if processor.Hash() == curProcessor.Hash() { + continue + } + } + + // 现有规则中没有rule以及有rule但hash不一致的场景,需要触发rule的update + processor.RecoverAlertCurEventFromDb() + s.ExternalProcessors.Processors[key] = processor + } + + for key := range s.ExternalProcessors.Processors { + if _, has := externalRuleWorkers[key]; !has { + delete(s.ExternalProcessors.Processors, key) + } + } + s.ExternalProcessors.ExternalLock.Unlock() +} diff --git a/alert/eval/eval.go b/alert/eval/eval.go new file mode 100644 index 0000000000000000000000000000000000000000..7f9b701196540a2b5d6f7830179d06820db12a66 --- /dev/null +++ b/alert/eval/eval.go @@ -0,0 +1,227 @@ +package eval + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/alert/common" + "github.com/ccfos/nightingale/v6/alert/process" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + promsdk "github.com/ccfos/nightingale/v6/pkg/prom" + "github.com/ccfos/nightingale/v6/prom" + + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/str" +) + +type AlertRuleWorker struct { + datasourceId int64 + quit chan struct{} + inhibit bool + severity int + + rule *models.AlertRule + + processor *process.Processor + + promClients *prom.PromClientMap + ctx *ctx.Context +} + +func NewAlertRuleWorker(rule *models.AlertRule, datasourceId int64, processor *process.Processor, promClients *prom.PromClientMap, ctx *ctx.Context) *AlertRuleWorker { + arw := &AlertRuleWorker{ + datasourceId: datasourceId, + quit: make(chan struct{}), + rule: rule, + processor: processor, + + promClients: promClients, + ctx: ctx, + } + + return arw +} + +func (arw *AlertRuleWorker) Key() string { + return common.RuleKey(arw.datasourceId, arw.rule.Id) +} + +func (arw *AlertRuleWorker) Hash() string { + return str.MD5(fmt.Sprintf("%d_%d_%s_%d", + arw.rule.Id, + arw.rule.PromEvalInterval, + arw.rule.RuleConfig, + arw.datasourceId, + )) +} + +func (arw *AlertRuleWorker) Prepare() { + arw.processor.RecoverAlertCurEventFromDb() +} + +func (arw *AlertRuleWorker) Start() { + logger.Infof("eval:%s started", arw.Key()) + interval := arw.rule.PromEvalInterval + if interval <= 0 { + interval = 10 + } + go func() { + for { + select { + case <-arw.quit: + return + default: + arw.Eval() + time.Sleep(time.Duration(interval) * time.Second) + } + } + }() +} + +func (arw *AlertRuleWorker) Eval() { + cachedRule := arw.rule + if cachedRule == nil { + logger.Errorf("rule_eval:%s rule not found", arw.Key()) + return + } + + typ := cachedRule.GetRuleType() + var lst []common.AnomalyPoint + switch typ { + case models.PROMETHEUS: + lst = arw.GetPromAnomalyPoint(cachedRule.RuleConfig) + case models.HOST: + lst = arw.GetHostAnomalyPoint(cachedRule.RuleConfig) + default: + return + } + + arw.processor.Handle(lst, "inner", arw.inhibit) +} + +func (arw *AlertRuleWorker) Stop() { + logger.Infof("%s stopped", arw.Key()) + close(arw.quit) +} + +func (arw *AlertRuleWorker) GetPromAnomalyPoint(ruleConfig string) []common.AnomalyPoint { + var lst []common.AnomalyPoint + var severity int + + var rule *models.PromRuleConfig + if err := json.Unmarshal([]byte(ruleConfig), &rule); err != nil { + logger.Errorf("rule_eval:%s rule_config:%s, error:%v", arw.Key(), ruleConfig, err) + return lst + } + + if rule == nil { + logger.Errorf("rule_eval:%s rule_config:%s, error:rule is nil", arw.Key(), ruleConfig) + return lst + } + + arw.inhibit = rule.Inhibit + for _, query := range rule.Queries { + if query.Severity < severity { + arw.severity = query.Severity + } + + promql := strings.TrimSpace(query.PromQl) + if promql == "" { + logger.Errorf("rule_eval:%s promql is blank", arw.Key()) + continue + } + + if arw.promClients.IsNil(arw.datasourceId) { + logger.Errorf("rule_eval:%s error reader client is nil", arw.Key()) + continue + } + + readerClient := arw.promClients.GetCli(arw.datasourceId) + + var warnings promsdk.Warnings + value, warnings, err := readerClient.Query(context.Background(), promql, time.Now()) + if err != nil { + logger.Errorf("rule_eval:%s promql:%s, error:%v", arw.Key(), promql, err) + continue + } + + if len(warnings) > 0 { + logger.Errorf("rule_eval:%s promql:%s, warnings:%v", arw.Key(), promql, warnings) + continue + } + + logger.Debugf("rule_eval:%s query:%+v, value:%v", arw.Key(), query, value) + points := common.ConvertAnomalyPoints(value) + for i := 0; i < len(points); i++ { + points[i].Severity = query.Severity + } + lst = append(lst, points...) + } + return lst +} + +func (arw *AlertRuleWorker) GetHostAnomalyPoint(ruleConfig string) []common.AnomalyPoint { + var lst []common.AnomalyPoint + var severity int + + var rule *models.HostRuleConfig + if err := json.Unmarshal([]byte(ruleConfig), &rule); err != nil { + logger.Errorf("rule_eval:%s rule_config:%s, error:%v", arw.Key(), ruleConfig, err) + return lst + } + + if rule == nil { + logger.Errorf("rule_eval:%s rule_config:%s, error:rule is nil", arw.Key(), ruleConfig) + return lst + } + + arw.inhibit = rule.Inhibit + now := time.Now().Unix() + for _, trigger := range rule.Triggers { + if trigger.Severity < severity { + arw.severity = trigger.Severity + } + + query := models.GetHostsQuery(rule.Queries) + switch trigger.Type { + case "target_miss", "offset": + t := now - int64(trigger.Duration) + if trigger.Type == "offset" { + t = int64(trigger.Duration) + } + + hosts, err := models.TargetGetsByFilter(arw.ctx, query, trigger.Type, t, 0, 0) + if err != nil { + logger.Errorf("rule_eval:%s query:%v, error:%v", arw.Key(), query, err) + continue + } + + for _, host := range hosts { + m := make(map[string]string) + m["ident"] = host.Ident + lst = append(lst, common.NewAnomalyPoint(trigger.Type, m, now, float64(t), trigger.Severity)) + } + case "pct_target_miss": + AllCount, err := models.TargetCountByFilter(arw.ctx, query, "", 0) + if err != nil { + logger.Errorf("rule_eval:%s query:%v, error:%v", arw.Key(), query, err) + continue + } + missCount, err := models.TargetCountByFilter(arw.ctx, query, trigger.Type, now-int64(trigger.Duration)) + if err != nil { + logger.Errorf("rule_eval:%s query:%v, error:%v", arw.Key(), query, err) + continue + } + + pct := float64(missCount) / float64(AllCount) * 100 + if pct >= float64(trigger.Percent) { + lst = append(lst, common.NewAnomalyPoint(trigger.Type, nil, now, pct, trigger.Severity)) + } + } + } + return lst +} diff --git a/src/server/engine/mute_strategy.go b/alert/mute/mute.go similarity index 59% rename from src/server/engine/mute_strategy.go rename to alert/mute/mute.go index 29278b571245c1eabd5ef6c51d44b521ef13d912..4f7d6a969b128811e17a7d46a1fa65eac3fc1b66 100644 --- a/src/server/engine/mute_strategy.go +++ b/alert/mute/mute.go @@ -1,31 +1,34 @@ -package engine +package mute import ( "strconv" "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/alert/common" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/memsto" + "github.com/toolkits/pkg/logger" ) -type MuteStrategyFunc func(rule *models.AlertRule, event *models.AlertCurEvent) bool +func IsMuted(rule *models.AlertRule, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType, alertMuteCache *memsto.AlertMuteCacheType) bool { + if TimeNonEffectiveMuteStrategy(rule, event) { + return true + } -var AlertMuteStrategies = []MuteStrategyFunc{ - TimeNonEffectiveMuteStrategy, - IdentNotExistsMuteStrategy, - BgNotMatchMuteStrategy, - EventMuteStrategy, -} + if IdentNotExistsMuteStrategy(rule, event, targetCache) { + return true + } -func IsMuted(rule *models.AlertRule, event *models.AlertCurEvent) bool { - for _, strategyFunc := range AlertMuteStrategies { - if strategyFunc(rule, event) { - return true - } + if BgNotMatchMuteStrategy(rule, event, targetCache) { + return true } + + if EventMuteStrategy(event, alertMuteCache) { + return true + } + return false } @@ -65,12 +68,12 @@ func TimeNonEffectiveMuteStrategy(rule *models.AlertRule, event *models.AlertCur } // IdentNotExistsMuteStrategy 根据ident是否存在过滤,如果ident不存在,则target_up的告警直接过滤掉 -func IdentNotExistsMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent) bool { +func IdentNotExistsMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType) bool { ident, has := event.TagsMap["ident"] if !has { return false } - _, exists := memsto.TargetCache.Get(ident) + _, exists := targetCache.Get(ident) // 如果是target_up的告警,且ident已经不存在了,直接过滤掉 // 这里的判断有点太粗暴了,但是目前没有更好的办法 if !exists && strings.Contains(rule.PromQl, "target_up") { @@ -81,7 +84,7 @@ func IdentNotExistsMuteStrategy(rule *models.AlertRule, event *models.AlertCurEv } // BgNotMatchMuteStrategy 当规则开启只在bg内部告警时,对于非bg内部的机器过滤 -func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent) bool { +func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType) bool { // 没有开启BG内部告警,直接不过滤 if rule.EnableInBG == 0 { return false @@ -92,7 +95,7 @@ func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent) return false } - target, exists := memsto.TargetCache.Get(ident) + target, exists := targetCache.Get(ident) // 对于包含ident的告警事件,check一下ident所属bg和rule所属bg是否相同 // 如果告警规则选择了只在本BG生效,那其他BG的机器就不能因此规则产生告警 if exists && target.GroupId != rule.GroupId { @@ -102,8 +105,8 @@ func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent) return false } -func EventMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent) bool { - mutes, has := memsto.AlertMuteCache.Gets(event.GroupId) +func EventMuteStrategy(event *models.AlertCurEvent, alertMuteCache *memsto.AlertMuteCacheType) bool { + mutes, has := alertMuteCache.Gets(event.GroupId) if !has || len(mutes) == 0 { return false } @@ -122,64 +125,54 @@ func matchMute(event *models.AlertCurEvent, mute *models.AlertMute, clock ...int if mute.Disabled == 1 { return false } - ts := event.TriggerTime if len(clock) > 0 { ts = clock[0] } - // 如果不是全局的,判断 cluster - if mute.Cluster != models.ClusterAll { - // mute.Cluster 是一个字符串,可能是多个cluster的组合,比如"cluster1 cluster2" - clusters := strings.Fields(mute.Cluster) - cm := make(map[string]struct{}, len(clusters)) - for i := 0; i < len(clusters); i++ { - cm[clusters[i]] = struct{}{} + // 如果不是全局的,判断 匹配的 datasource id + if !(len(mute.DatasourceIdsJson) != 0 && mute.DatasourceIdsJson[0] == 0) && event.DatasourceId != 0 { + idm := make(map[int64]struct{}, len(mute.DatasourceIdsJson)) + for i := 0; i < len(mute.DatasourceIdsJson); i++ { + idm[mute.DatasourceIdsJson[i]] = struct{}{} } - // 判断event.Cluster是否包含在cm中 - if _, has := cm[event.Cluster]; !has { + // 判断 event.datasourceId 是否包含在 idm 中 + if _, has := idm[event.DatasourceId]; !has { return false } } - if ts < mute.Btime || ts > mute.Etime { - return false - } - - return matchTags(event.TagsMap, mute.ITags) -} - -func matchTag(value string, filter models.TagFilter) bool { - switch filter.Func { - case "==": - return filter.Value == value - case "!=": - return filter.Value != value - case "in": - _, has := filter.Vset[value] - return has - case "not in": - _, has := filter.Vset[value] - return !has - case "=~": - return filter.Regexp.MatchString(value) - case "!~": - return !filter.Regexp.MatchString(value) - } - // unexpect func - return false -} - -func matchTags(eventTagsMap map[string]string, itags []models.TagFilter) bool { - for _, filter := range itags { - value, has := eventTagsMap[filter.Key] - if !has { + var matchTime bool + if mute.MuteTimeType == models.TimeRange { + if ts < mute.Btime || ts > mute.Etime { return false } - if !matchTag(value, filter) { - return false + matchTime = true + } else if mute.MuteTimeType == models.Periodic { + tm := time.Unix(event.TriggerTime, 0) + triggerTime := tm.Format("15:04") + triggerWeek := strconv.Itoa(int(tm.Weekday())) + + for i := 0; i < len(mute.PeriodicMutesJson); i++ { + if strings.Contains(mute.PeriodicMutesJson[i].EnableDaysOfWeek, triggerWeek) { + if mute.PeriodicMutesJson[i].EnableStime <= mute.PeriodicMutesJson[i].EnableEtime { + if triggerTime >= mute.PeriodicMutesJson[i].EnableStime && triggerTime < mute.PeriodicMutesJson[i].EnableEtime { + matchTime = true + break + } + } else { + if triggerTime < mute.PeriodicMutesJson[i].EnableStime || triggerTime >= mute.PeriodicMutesJson[i].EnableEtime { + matchTime = true + break + } + } + } } } - return true + if !matchTime { + return false + } + + return common.MatchTags(event.TagsMap, mute.ITags) } diff --git a/alert/naming/hashring.go b/alert/naming/hashring.go new file mode 100644 index 0000000000000000000000000000000000000000..87e6569b4aaceaacccbbf6e78a32c7d61aca4345 --- /dev/null +++ b/alert/naming/hashring.go @@ -0,0 +1,64 @@ +package naming + +import ( + "sync" + + "github.com/toolkits/pkg/consistent" + "github.com/toolkits/pkg/logger" +) + +const NodeReplicas = 500 + +type DatasourceHashRingType struct { + sync.RWMutex + Rings map[int64]*consistent.Consistent +} + +// for alert_rule sharding +var DatasourceHashRing = DatasourceHashRingType{Rings: make(map[int64]*consistent.Consistent)} + +func NewConsistentHashRing(replicas int32, nodes []string) *consistent.Consistent { + ret := consistent.New() + ret.NumberOfReplicas = int(replicas) + for i := 0; i < len(nodes); i++ { + ret.Add(nodes[i]) + } + return ret +} + +func RebuildConsistentHashRing(datasourceId int64, nodes []string) { + r := consistent.New() + r.NumberOfReplicas = NodeReplicas + for i := 0; i < len(nodes); i++ { + r.Add(nodes[i]) + } + + DatasourceHashRing.Set(datasourceId, r) + logger.Infof("hash ring %d rebuild %+v", datasourceId, r.Members()) +} + +func (chr *DatasourceHashRingType) GetNode(datasourceId int64, pk string) (string, error) { + chr.RLock() + defer chr.RUnlock() + _, exists := chr.Rings[datasourceId] + if !exists { + chr.Rings[datasourceId] = NewConsistentHashRing(int32(NodeReplicas), []string{}) + } + + return chr.Rings[datasourceId].Get(pk) +} + +func (chr *DatasourceHashRingType) IsHit(datasourceId int64, pk string, currentNode string) bool { + node, err := chr.GetNode(datasourceId, pk) + if err != nil { + logger.Debugf("datasource id:%d pk:%s failed to get node from hashring:%v", datasourceId, pk, err) + return false + } + return node == currentNode +} + +func (chr *DatasourceHashRingType) Set(datasourceId int64, r *consistent.Consistent) { + chr.RLock() + defer chr.RUnlock() + chr.Rings[datasourceId] = r +} diff --git a/alert/naming/heartbeat.go b/alert/naming/heartbeat.go new file mode 100644 index 0000000000000000000000000000000000000000..d21c8a6a2e8e515088fe23ffec69c026bb6a7a21 --- /dev/null +++ b/alert/naming/heartbeat.go @@ -0,0 +1,128 @@ +package naming + +import ( + "fmt" + "sort" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + + "github.com/toolkits/pkg/logger" +) + +type Naming struct { + ctx *ctx.Context + Heartbeat aconf.HeartbeatConfig +} + +func NewNaming(ctx *ctx.Context, heartbeat aconf.HeartbeatConfig) *Naming { + naming := &Naming{ + ctx: ctx, + Heartbeat: heartbeat, + } + naming.Heartbeats() + return naming +} + +// local servers +var localss map[int64]string + +func (n *Naming) Heartbeats() error { + localss = make(map[int64]string) + if err := n.heartbeat(); err != nil { + fmt.Println("failed to heartbeat:", err) + return err + } + + go n.loopHeartbeat() + go n.loopDeleteInactiveInstances() + return nil +} + +func (n *Naming) loopDeleteInactiveInstances() { + interval := time.Duration(10) * time.Minute + for { + time.Sleep(interval) + n.DeleteInactiveInstances() + } +} + +func (n *Naming) DeleteInactiveInstances() { + err := models.DB(n.ctx).Where("clock < ?", time.Now().Unix()-600).Delete(new(models.AlertingEngines)).Error + if err != nil { + logger.Errorf("delete inactive instances err:%v", err) + } +} + +func (n *Naming) loopHeartbeat() { + interval := time.Duration(n.Heartbeat.Interval) * time.Millisecond + for { + time.Sleep(interval) + if err := n.heartbeat(); err != nil { + logger.Warning(err) + } + } +} + +func (n *Naming) heartbeat() error { + var datasourceIds []int64 + var err error + + // 在页面上维护实例和集群的对应关系 + datasourceIds, err = models.GetDatasourceIdsByClusterName(n.ctx, n.Heartbeat.ClusterName) + if err != nil { + return err + } + + if len(datasourceIds) == 0 { + err := models.AlertingEngineHeartbeatWithCluster(n.ctx, n.Heartbeat.Endpoint, n.Heartbeat.ClusterName, 0) + if err != nil { + logger.Warningf("heartbeat with cluster %s err:%v", "", err) + } + } else { + for i := 0; i < len(datasourceIds); i++ { + err := models.AlertingEngineHeartbeatWithCluster(n.ctx, n.Heartbeat.Endpoint, n.Heartbeat.ClusterName, datasourceIds[i]) + if err != nil { + logger.Warningf("heartbeat with cluster %d err:%v", datasourceIds[i], err) + } + } + } + + for i := 0; i < len(datasourceIds); i++ { + servers, err := n.ActiveServers(datasourceIds[i]) + if err != nil { + logger.Warningf("hearbeat %d get active server err:%v", datasourceIds[i], err) + continue + } + + sort.Strings(servers) + newss := strings.Join(servers, " ") + + oldss, exists := localss[datasourceIds[i]] + if exists && oldss == newss { + continue + } + + RebuildConsistentHashRing(datasourceIds[i], servers) + localss[datasourceIds[i]] = newss + } + + return nil +} + +func (n *Naming) ActiveServers(datasourceId int64) ([]string, error) { + if datasourceId == -1 { + return nil, fmt.Errorf("cluster is empty") + } + + // 30秒内有心跳,就认为是活的 + return models.AlertingEngineGetsInstances(n.ctx, "datasource_id = ? and clock > ?", datasourceId, time.Now().Unix()-30) +} + +func (n *Naming) AllActiveServers() ([]string, error) { + // 30秒内有心跳,就认为是活的 + return models.AlertingEngineGetsInstances(n.ctx, "clock > ?", time.Now().Unix()-30) +} diff --git a/src/server/naming/leader.go b/alert/naming/leader.go similarity index 50% rename from src/server/naming/leader.go rename to alert/naming/leader.go index 94b080eebbe90d3b0eacbe641f195d7d7d9994d9..15192c9a13a9881190d1b89fb19505c164ea6752 100644 --- a/src/server/naming/leader.go +++ b/alert/naming/leader.go @@ -3,23 +3,22 @@ package naming import ( "sort" - "github.com/didi/nightingale/v5/src/server/config" "github.com/toolkits/pkg/logger" ) -func IamLeader(cluster string) (bool, error) { - servers, err := ActiveServers(cluster) +func (n *Naming) IamLeader() bool { + servers, err := n.AllActiveServers() if err != nil { logger.Errorf("failed to get active servers: %v", err) - return false, err + return false } if len(servers) == 0 { logger.Errorf("active servers empty") - return false, err + return false } sort.Strings(servers) - return config.C.Heartbeat.Endpoint == servers[0], nil + return n.Heartbeat.Endpoint == servers[0] } diff --git a/alert/process/alert_cur_event.go b/alert/process/alert_cur_event.go new file mode 100644 index 0000000000000000000000000000000000000000..651ee229a479a733fcac05f8e5dcc2eca1d6856b --- /dev/null +++ b/alert/process/alert_cur_event.go @@ -0,0 +1,74 @@ +package process + +import ( + "sync" + + "github.com/ccfos/nightingale/v6/models" +) + +type AlertCurEventMap struct { + sync.RWMutex + Data map[string]*models.AlertCurEvent +} + +func NewAlertCurEventMap(data map[string]*models.AlertCurEvent) *AlertCurEventMap { + if data == nil { + return &AlertCurEventMap{ + Data: make(map[string]*models.AlertCurEvent), + } + } + return &AlertCurEventMap{ + Data: data, + } +} + +func (a *AlertCurEventMap) SetAll(data map[string]*models.AlertCurEvent) { + a.Lock() + defer a.Unlock() + a.Data = data +} + +func (a *AlertCurEventMap) Set(key string, value *models.AlertCurEvent) { + a.Lock() + defer a.Unlock() + a.Data[key] = value +} + +func (a *AlertCurEventMap) Get(key string) (*models.AlertCurEvent, bool) { + a.RLock() + defer a.RUnlock() + event, exists := a.Data[key] + return event, exists +} + +func (a *AlertCurEventMap) UpdateLastEvalTime(key string, lastEvalTime int64) { + a.Lock() + defer a.Unlock() + event, exists := a.Data[key] + if !exists { + return + } + event.LastEvalTime = lastEvalTime +} + +func (a *AlertCurEventMap) Delete(key string) { + a.Lock() + defer a.Unlock() + delete(a.Data, key) +} + +func (a *AlertCurEventMap) Keys() []string { + a.RLock() + defer a.RUnlock() + keys := make([]string, 0, len(a.Data)) + for k := range a.Data { + keys = append(keys, k) + } + return keys +} + +func (a *AlertCurEventMap) GetAll() map[string]*models.AlertCurEvent { + a.RLock() + defer a.RUnlock() + return a.Data +} diff --git a/alert/process/process.go b/alert/process/process.go new file mode 100644 index 0000000000000000000000000000000000000000..bc1bc54ed0288387df2cfab527b07ad6fe0a6126 --- /dev/null +++ b/alert/process/process.go @@ -0,0 +1,430 @@ +package process + +import ( + "bytes" + "fmt" + "html/template" + "sort" + "strings" + "sync" + "time" + + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/ccfos/nightingale/v6/alert/common" + "github.com/ccfos/nightingale/v6/alert/dispatch" + "github.com/ccfos/nightingale/v6/alert/mute" + "github.com/ccfos/nightingale/v6/alert/queue" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/tplx" + "github.com/ccfos/nightingale/v6/prom" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/str" +) + +type ExternalProcessorsType struct { + ExternalLock sync.RWMutex + Processors map[string]*Processor +} + +var ExternalProcessors ExternalProcessorsType + +func NewExternalProcessors() *ExternalProcessorsType { + return &ExternalProcessorsType{ + Processors: make(map[string]*Processor), + } +} + +func (e *ExternalProcessorsType) GetExternalAlertRule(datasourceId, id int64) (*Processor, bool) { + e.ExternalLock.RLock() + defer e.ExternalLock.RUnlock() + processor, has := e.Processors[common.RuleKey(datasourceId, id)] + return processor, has +} + +type Processor struct { + datasourceId int64 + quit chan struct{} + + rule *models.AlertRule + fires *AlertCurEventMap + pendings *AlertCurEventMap + inhibit bool + + tagsMap map[string]string + tagsArr []string + target string + targetNote string + groupName string + + atertRuleCache *memsto.AlertRuleCacheType + targetCache *memsto.TargetCacheType + busiGroupCache *memsto.BusiGroupCacheType + alertMuteCache *memsto.AlertMuteCacheType + + promClients *prom.PromClientMap + ctx *ctx.Context + stats *astats.Stats +} + +func (arw *Processor) Key() string { + return common.RuleKey(arw.datasourceId, arw.rule.Id) +} + +func (arw *Processor) Hash() string { + return str.MD5(fmt.Sprintf("%d_%d_%s_%d", + arw.rule.Id, + arw.rule.PromEvalInterval, + arw.rule.RuleConfig, + arw.datasourceId, + )) +} + +func NewProcessor(rule *models.AlertRule, datasourceId int64, atertRuleCache *memsto.AlertRuleCacheType, targetCache *memsto.TargetCacheType, + busiGroupCache *memsto.BusiGroupCacheType, alertMuteCache *memsto.AlertMuteCacheType, promClients *prom.PromClientMap, ctx *ctx.Context, + stats *astats.Stats) *Processor { + + arw := &Processor{ + datasourceId: datasourceId, + quit: make(chan struct{}), + rule: rule, + + targetCache: targetCache, + busiGroupCache: busiGroupCache, + alertMuteCache: alertMuteCache, + atertRuleCache: atertRuleCache, + + promClients: promClients, + ctx: ctx, + stats: stats, + } + + arw.mayHandleGroup() + return arw +} + +func (arw *Processor) Handle(anomalyPoints []common.AnomalyPoint, from string, inhibit bool) { + // 有可能rule的一些配置已经发生变化,比如告警接收人、callbacks等 + // 这些信息的修改是不会引起worker restart的,但是确实会影响告警处理逻辑 + // 所以,这里直接从memsto.AlertRuleCache中获取并覆盖 + arw.inhibit = inhibit + arw.rule = arw.atertRuleCache.Get(arw.rule.Id) + cachedRule := arw.rule + if cachedRule == nil { + logger.Errorf("rule_eval:%s rule not found", arw.Key()) + return + } + + now := time.Now().Unix() + alertingKeys := map[string]struct{}{} + + // 根据 event 的 tag 将 events 分组,处理告警抑制的情况 + eventsMap := make(map[string][]*models.AlertCurEvent) + for _, anomalyPoint := range anomalyPoints { + event := arw.BuildEvent(anomalyPoint, from, now) + // 如果 event 被 mute 了,本质也是 fire 的状态,这里无论如何都添加到 alertingKeys 中,防止 fire 的事件自动恢复了 + hash := event.Hash + alertingKeys[hash] = struct{}{} + if mute.IsMuted(cachedRule, event, arw.targetCache, arw.alertMuteCache) { + logger.Debugf("rule_eval:%s event:%v is muted", arw.Key(), event) + continue + } + tagHash := TagHash(anomalyPoint) + eventsMap[tagHash] = append(eventsMap[tagHash], event) + } + + for _, events := range eventsMap { + arw.handleEvent(events) + } + + arw.HandleRecover(alertingKeys, now) +} + +func (arw *Processor) BuildEvent(anomalyPoint common.AnomalyPoint, from string, now int64) *models.AlertCurEvent { + arw.fillTags(anomalyPoint) + arw.mayHandleIdent() + hash := Hash(arw.rule.Id, arw.datasourceId, anomalyPoint) + + event := arw.rule.GenerateNewEvent(arw.ctx) + event.TriggerTime = anomalyPoint.Timestamp + event.TagsMap = arw.tagsMap + event.DatasourceId = arw.datasourceId + event.Hash = hash + event.TargetIdent = arw.target + event.TargetNote = arw.targetNote + event.TriggerValue = anomalyPoint.ReadableValue() + event.TagsJSON = arw.tagsArr + event.GroupName = arw.groupName + event.Tags = strings.Join(arw.tagsArr, ",,") + event.IsRecovered = false + event.Callbacks = arw.rule.Callbacks + event.CallbacksJSON = arw.rule.CallbacksJSON + event.Annotations = arw.rule.Annotations + event.AnnotationsJSON = arw.rule.AnnotationsJSON + event.RuleConfig = arw.rule.RuleConfig + event.RuleConfigJson = arw.rule.RuleConfigJson + event.Severity = anomalyPoint.Severity + + if from == "inner" { + event.LastEvalTime = now + } else { + event.LastEvalTime = event.TriggerTime + } + return event +} + +func (arw *Processor) HandleRecover(alertingKeys map[string]struct{}, now int64) { + for _, hash := range arw.pendings.Keys() { + if _, has := alertingKeys[hash]; has { + continue + } + arw.pendings.Delete(hash) + } + + for hash := range arw.fires.GetAll() { + if _, has := alertingKeys[hash]; has { + continue + } + arw.RecoverSingle(hash, now, nil) + } +} + +func (arw *Processor) RecoverSingle(hash string, now int64, value *string) { + cachedRule := arw.rule + if cachedRule == nil { + return + } + event, has := arw.fires.Get(hash) + if !has { + return + } + // 如果配置了留观时长,就不能立马恢复了 + if cachedRule.RecoverDuration > 0 && now-event.LastEvalTime < cachedRule.RecoverDuration { + logger.Debugf("rule_eval:%s event:%v not recover", arw.Key(), event) + return + } + if value != nil { + event.TriggerValue = *value + } + + // 没查到触发阈值的vector,姑且就认为这个vector的值恢复了 + // 我确实无法分辨,是prom中有值但是未满足阈值所以没返回,还是prom中确实丢了一些点导致没有数据可以返回,尴尬 + arw.fires.Delete(hash) + arw.pendings.Delete(hash) + + // 可能是因为调整了promql才恢复的,所以事件里边要体现最新的promql,否则用户会比较困惑 + // 当然,其实rule的各个字段都可能发生变化了,都更新一下吧 + cachedRule.UpdateEvent(event) + event.IsRecovered = true + event.LastEvalTime = now + arw.pushEventToQueue(event) +} + +func (arw *Processor) handleEvent(events []*models.AlertCurEvent) { + var fireEvents []*models.AlertCurEvent + // severity 初始为 4, 一定为遇到比自己优先级高的事件 + severity := 4 + for _, event := range events { + if event == nil { + continue + } + if arw.rule.PromForDuration == 0 { + fireEvents = append(fireEvents, event) + if severity > event.Severity { + severity = event.Severity + } + continue + } + + var preTriggerTime int64 + preEvent, has := arw.pendings.Get(event.Hash) + if has { + arw.pendings.UpdateLastEvalTime(event.Hash, event.LastEvalTime) + preTriggerTime = preEvent.TriggerTime + } else { + arw.pendings.Set(event.Hash, event) + preTriggerTime = event.TriggerTime + } + + if event.LastEvalTime-preTriggerTime+int64(event.PromEvalInterval) >= int64(arw.rule.PromForDuration) { + fireEvents = append(fireEvents, event) + if severity > event.Severity { + severity = event.Severity + } + continue + } + } + + arw.inhibitEvent(fireEvents, severity) +} + +func (arw *Processor) inhibitEvent(events []*models.AlertCurEvent, highSeverity int) { + for _, event := range events { + if arw.inhibit && event.Severity > highSeverity { + logger.Debugf("rule_eval:%s event:%+v inhibit highSeverity:%d", arw.Key(), event, highSeverity) + continue + } + arw.fireEvent(event) + } +} + +func (arw *Processor) fireEvent(event *models.AlertCurEvent) { + // As arw.rule maybe outdated, use rule from cache + cachedRule := arw.rule + if cachedRule == nil { + return + } + logger.Debugf("rule_eval:%s event:%+v fire", arw.Key(), event) + if fired, has := arw.fires.Get(event.Hash); has { + arw.fires.UpdateLastEvalTime(event.Hash, event.LastEvalTime) + + if cachedRule.NotifyRepeatStep == 0 { + logger.Debugf("rule_eval:%s event:%+v repeat is zero nothing to do", arw.Key(), event) + // 说明不想重复通知,那就直接返回了,nothing to do + // do not need to send alert again + return + } + + // 之前发送过告警了,这次是否要继续发送,要看是否过了通道静默时间 + if event.LastEvalTime > fired.LastSentTime+int64(cachedRule.NotifyRepeatStep)*60 { + if cachedRule.NotifyMaxNumber == 0 { + // 最大可以发送次数如果是0,表示不想限制最大发送次数,一直发即可 + event.NotifyCurNumber = fired.NotifyCurNumber + 1 + event.FirstTriggerTime = fired.FirstTriggerTime + arw.pushEventToQueue(event) + } else { + // 有最大发送次数的限制,就要看已经发了几次了,是否达到了最大发送次数 + if fired.NotifyCurNumber >= cachedRule.NotifyMaxNumber { + logger.Debugf("rule_eval:%s event:%+v reach max number", arw.Key(), event) + return + } else { + event.NotifyCurNumber = fired.NotifyCurNumber + 1 + event.FirstTriggerTime = fired.FirstTriggerTime + arw.pushEventToQueue(event) + } + } + } + } else { + event.NotifyCurNumber = 1 + event.FirstTriggerTime = event.TriggerTime + arw.pushEventToQueue(event) + } +} + +func (arw *Processor) pushEventToQueue(e *models.AlertCurEvent) { + if !e.IsRecovered { + e.LastSentTime = e.LastEvalTime + arw.fires.Set(e.Hash, e) + } + + arw.stats.CounterAlertsTotal.WithLabelValues(fmt.Sprintf("%d", e.DatasourceId)).Inc() + dispatch.LogEvent(e, "push_queue") + if !queue.EventQueue.PushFront(e) { + logger.Warningf("event_push_queue: queue is full, event:%+v", e) + } +} + +func (arw *Processor) RecoverAlertCurEventFromDb() { + arw.pendings = NewAlertCurEventMap(nil) + + curEvents, err := models.AlertCurEventGetByRuleIdAndCluster(arw.ctx, arw.rule.Id, arw.datasourceId) + if err != nil { + logger.Errorf("recover event from db for rule:%s failed, err:%s", arw.Key(), err) + arw.fires = NewAlertCurEventMap(nil) + return + } + + fireMap := make(map[string]*models.AlertCurEvent) + for _, event := range curEvents { + event.DB2Mem() + fireMap[event.Hash] = event + } + + arw.fires = NewAlertCurEventMap(fireMap) +} + +func (arw *Processor) fillTags(anomalyPoint common.AnomalyPoint) { + // handle series tags + tagsMap := make(map[string]string) + for label, value := range anomalyPoint.Labels { + tagsMap[string(label)] = string(value) + } + + var e = &models.AlertCurEvent{ + TagsMap: tagsMap, + } + + // handle rule tags + for _, tag := range arw.rule.AppendTagsJSON { + arr := strings.SplitN(tag, "=", 2) + + var defs = []string{ + "{{$labels := .TagsMap}}", + "{{$value := .TriggerValue}}", + } + tagValue := arr[1] + text := strings.Join(append(defs, tagValue), "") + t, err := template.New(fmt.Sprint(arw.rule.Id)).Funcs(template.FuncMap(tplx.TemplateFuncMap)).Parse(text) + if err != nil { + tagValue = fmt.Sprintf("parse tag value failed, err:%s", err) + } + + var body bytes.Buffer + err = t.Execute(&body, e) + if err != nil { + tagValue = fmt.Sprintf("parse tag value failed, err:%s", err) + } + + if err == nil { + tagValue = body.String() + } + tagsMap[arr[0]] = tagValue + } + + tagsMap["rulename"] = arw.rule.Name + arw.tagsMap = tagsMap + + // handle tagsArr + arw.tagsArr = labelMapToArr(tagsMap) +} + +func (arw *Processor) mayHandleIdent() { + // handle ident + if ident, has := arw.tagsMap["ident"]; has { + if target, exists := arw.targetCache.Get(ident); exists { + arw.target = target.Ident + arw.targetNote = target.Note + } + } +} + +func (arw *Processor) mayHandleGroup() { + // handle bg + bg := arw.busiGroupCache.GetByBusiGroupId(arw.rule.GroupId) + if bg != nil { + arw.groupName = bg.Name + } +} + +func labelMapToArr(m map[string]string) []string { + numLabels := len(m) + + labelStrings := make([]string, 0, numLabels) + for label, value := range m { + labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", label, value)) + } + + if numLabels > 1 { + sort.Strings(labelStrings) + } + return labelStrings +} + +func Hash(ruleId, datasourceId int64, vector common.AnomalyPoint) string { + return str.MD5(fmt.Sprintf("%d_%s_%d_%d", ruleId, vector.Labels.String(), datasourceId, vector.Severity)) +} + +func TagHash(vector common.AnomalyPoint) string { + return str.MD5(vector.Labels.String()) +} diff --git a/alert/queue/queue.go b/alert/queue/queue.go new file mode 100644 index 0000000000000000000000000000000000000000..ea2218b876073cedbe27f4d0da4a0a9e6a0f6003 --- /dev/null +++ b/alert/queue/queue.go @@ -0,0 +1,18 @@ +package queue + +import ( + "time" + + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/toolkits/pkg/container/list" +) + +var EventQueue = list.NewSafeListLimited(10000000) + +func ReportQueueSize(stats *astats.Stats) { + for { + time.Sleep(time.Second) + + stats.GaugeAlertQueueSize.Set(float64(EventQueue.Len())) + } +} diff --git a/src/server/engine/rule_record.go b/alert/record/prom_rule.go similarity index 54% rename from src/server/engine/rule_record.go rename to alert/record/prom_rule.go index 1043543d2a35ec6547977e94dbd3618c34bf4ee9..433fe58312c8756e3cee379965f816970a66a770 100644 --- a/src/server/engine/rule_record.go +++ b/alert/record/prom_rule.go @@ -1,4 +1,4 @@ -package engine +package record import ( "context" @@ -6,40 +6,43 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/prom" + "github.com/ccfos/nightingale/v6/pushgw/writer" + "github.com/toolkits/pkg/logger" "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/common/conv" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/writer" ) type RecordRuleContext struct { - cluster string - quit chan struct{} + datasourceId int64 + quit chan struct{} rule *models.RecordingRule + // writers *writer.WritersType + promClients *prom.PromClientMap } -func NewRecordRuleContext(rule *models.RecordingRule, cluster string) *RecordRuleContext { +func NewRecordRuleContext(rule *models.RecordingRule, datasourceId int64, promClients *prom.PromClientMap, writers *writer.WritersType) *RecordRuleContext { return &RecordRuleContext{ - cluster: cluster, - quit: make(chan struct{}), - rule: rule, + datasourceId: datasourceId, + quit: make(chan struct{}), + rule: rule, + promClients: promClients, + //writers: writers, } } func (rrc *RecordRuleContext) Key() string { - return fmt.Sprintf("record-%s-%d", rrc.cluster, rrc.rule.Id) + return fmt.Sprintf("record-%d-%d", rrc.datasourceId, rrc.rule.Id) } func (rrc *RecordRuleContext) Hash() string { - return str.MD5(fmt.Sprintf("%d_%d_%s_%s", + return str.MD5(fmt.Sprintf("%d_%d_%s_%d", rrc.rule.Id, rrc.rule.PromEvalInterval, rrc.rule.PromQl, - rrc.cluster, + rrc.datasourceId, )) } @@ -71,26 +74,25 @@ func (rrc *RecordRuleContext) Eval() { return } - if config.ReaderClients.IsNil(rrc.cluster) { + if rrc.promClients.IsNil(rrc.datasourceId) { logger.Errorf("eval:%s reader client is nil", rrc.Key()) return } - value, warnings, err := config.ReaderClients.GetCli(rrc.cluster).Query(context.Background(), promql, time.Now()) + value, warnings, err := rrc.promClients.GetCli(rrc.datasourceId).Query(context.Background(), promql, time.Now()) if err != nil { - logger.Errorf("eval:%d promql:%s, error:%v", rrc.Key(), promql, err) + logger.Errorf("eval:%s promql:%s, error:%v", rrc.Key(), promql, err) return } if len(warnings) > 0 { - logger.Errorf("eval:%d promql:%s, warnings:%v", rrc.Key(), promql, warnings) + logger.Errorf("eval:%s promql:%s, warnings:%v", rrc.Key(), promql, warnings) return } - ts := conv.ConvertToTimeSeries(value, rrc.rule) + + ts := ConvertToTimeSeries(value, rrc.rule) if len(ts) != 0 { - for _, v := range ts { - writer.Writers.PushSample(rrc.rule.Name, v, rrc.cluster) - } + rrc.promClients.GetWriterCli(rrc.datasourceId).Write(ts) } } diff --git a/src/server/common/conv/sample.go b/alert/record/sample.go similarity index 97% rename from src/server/common/conv/sample.go rename to alert/record/sample.go index 97c1c15415987b476948becdaf7b898568a61aee..56e2870bdddf51d2dc10353a30ba8ef0b07595d0 100644 --- a/src/server/common/conv/sample.go +++ b/alert/record/sample.go @@ -1,11 +1,12 @@ -package conv +package record import ( "math" "strings" "time" - "github.com/didi/nightingale/v5/src/models" + "github.com/ccfos/nightingale/v6/models" + "github.com/prometheus/common/model" "github.com/prometheus/prometheus/prompb" ) diff --git a/alert/record/scheduler.go b/alert/record/scheduler.go new file mode 100644 index 0000000000000000000000000000000000000000..bf43d3454aca9e6663add27065744469483bd2a8 --- /dev/null +++ b/alert/record/scheduler.go @@ -0,0 +1,94 @@ +package record + +import ( + "context" + "fmt" + "time" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/ccfos/nightingale/v6/alert/naming" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/prom" + "github.com/ccfos/nightingale/v6/pushgw/writer" +) + +type Scheduler struct { + // key: hash + recordRules map[string]*RecordRuleContext + + aconf aconf.Alert + + recordingRuleCache *memsto.RecordingRuleCacheType + + promClients *prom.PromClientMap + writers *writer.WritersType + + stats *astats.Stats +} + +func NewScheduler(aconf aconf.Alert, rrc *memsto.RecordingRuleCacheType, promClients *prom.PromClientMap, writers *writer.WritersType, stats *astats.Stats) *Scheduler { + scheduler := &Scheduler{ + aconf: aconf, + recordRules: make(map[string]*RecordRuleContext), + + recordingRuleCache: rrc, + + promClients: promClients, + writers: writers, + + stats: stats, + } + + go scheduler.LoopSyncRules(context.Background()) + return scheduler +} + +func (s *Scheduler) LoopSyncRules(ctx context.Context) { + time.Sleep(time.Duration(s.aconf.EngineDelay) * time.Second) + duration := 9000 * time.Millisecond + for { + select { + case <-ctx.Done(): + return + case <-time.After(duration): + s.syncRecordRules() + } + } +} + +func (s *Scheduler) syncRecordRules() { + ids := s.recordingRuleCache.GetRuleIds() + recordRules := make(map[string]*RecordRuleContext) + for _, id := range ids { + rule := s.recordingRuleCache.Get(id) + if rule == nil { + continue + } + + datasourceIds := s.promClients.Hit(rule.DatasourceIdsJson) + for _, dsId := range datasourceIds { + if !naming.DatasourceHashRing.IsHit(dsId, fmt.Sprintf("%d", rule.Id), s.aconf.Heartbeat.Endpoint) { + continue + } + + recordRule := NewRecordRuleContext(rule, dsId, s.promClients, s.writers) + recordRules[recordRule.Hash()] = recordRule + } + } + + for hash, rule := range recordRules { + if _, has := s.recordRules[hash]; !has { + rule.Prepare() + rule.Start() + s.recordRules[hash] = rule + } + } + + for hash, rule := range s.recordRules { + if _, has := recordRules[hash]; !has { + rule.Stop() + delete(s.recordRules, hash) + } + } +} diff --git a/alert/router/router.go b/alert/router/router.go new file mode 100644 index 0000000000000000000000000000000000000000..4cdaa69156e41dad19ee2c9e290b67d709fd2409 --- /dev/null +++ b/alert/router/router.go @@ -0,0 +1,71 @@ +package router + +import ( + "net/http" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/ccfos/nightingale/v6/alert/process" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/httpx" + + "github.com/gin-gonic/gin" +) + +type Router struct { + HTTP httpx.Config + Alert aconf.Alert + AlertMuteCache *memsto.AlertMuteCacheType + TargetCache *memsto.TargetCacheType + BusiGroupCache *memsto.BusiGroupCacheType + AlertStats *astats.Stats + Ctx *ctx.Context + ExternalProcessors *process.ExternalProcessorsType +} + +func New(httpConfig httpx.Config, alert aconf.Alert, amc *memsto.AlertMuteCacheType, tc *memsto.TargetCacheType, bgc *memsto.BusiGroupCacheType, + astats *astats.Stats, ctx *ctx.Context, externalProcessors *process.ExternalProcessorsType) *Router { + return &Router{ + HTTP: httpConfig, + Alert: alert, + AlertStats: astats, + AlertMuteCache: amc, + TargetCache: tc, + BusiGroupCache: bgc, + Ctx: ctx, + ExternalProcessors: externalProcessors, + } +} + +func (rt *Router) Config(r *gin.Engine) { + service := r.Group("/v1/n9e") + service.POST("/event", rt.pushEventToQueue) + service.POST("/make-event", rt.makeEvent) +} + +func Render(c *gin.Context, data, msg interface{}) { + if msg == nil { + if data == nil { + data = struct{}{} + } + c.JSON(http.StatusOK, gin.H{"data": data, "error": ""}) + } else { + c.JSON(http.StatusOK, gin.H{"error": gin.H{"message": msg}}) + } +} + +func Dangerous(c *gin.Context, v interface{}, code ...int) { + if v == nil { + return + } + + switch t := v.(type) { + case string: + if t != "" { + c.JSON(http.StatusOK, gin.H{"error": gin.H{"message": v}}) + } + case error: + c.JSON(http.StatusOK, gin.H{"error": gin.H{"message": t.Error()}}) + } +} diff --git a/src/server/router/router_event.go b/alert/router/router_event.go similarity index 63% rename from src/server/router/router_event.go rename to alert/router/router_event.go index 899dc1d2353c97a29c3bc112c3b1b1ce70658d04..e8cd47d624ef5facdd2c48e8effd1f88564ce9b4 100644 --- a/src/server/router/router_event.go +++ b/alert/router/router_event.go @@ -5,20 +5,21 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/alert/common" + "github.com/ccfos/nightingale/v6/alert/dispatch" + "github.com/ccfos/nightingale/v6/alert/mute" + "github.com/ccfos/nightingale/v6/alert/naming" + "github.com/ccfos/nightingale/v6/alert/process" + "github.com/ccfos/nightingale/v6/alert/queue" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/poster" - "github.com/didi/nightingale/v5/src/server/common/conv" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/engine" - "github.com/didi/nightingale/v5/src/server/naming" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) -func pushEventToQueue(c *gin.Context) { +func (rt *Router) pushEventToQueue(c *gin.Context) { var event *models.AlertCurEvent ginx.BindJSON(c, &event) if event.RuleId == 0 { @@ -40,7 +41,7 @@ func pushEventToQueue(c *gin.Context) { event.TagsMap[arr[0]] = arr[1] } - if engine.EventMuteStrategy(nil, event) { + if mute.EventMuteStrategy(event, rt.AlertMuteCache) { logger.Infof("event_muted: rule_id=%d %s", event.RuleId, event.Hash) ginx.NewRender(c).Message(nil) return @@ -54,6 +55,10 @@ func pushEventToQueue(c *gin.Context) { event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err) } + if err := event.ParseRule("annotations"); err != nil { + event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err) + } + // 如果 rule_note 中有 ; 前缀,则使用 rule_note 替换 tags 中的内容 if strings.HasPrefix(event.RuleNote, ";") { event.RuleNote = strings.TrimPrefix(event.RuleNote, ";") @@ -67,10 +72,10 @@ func pushEventToQueue(c *gin.Context) { event.NotifyChannels = strings.Join(event.NotifyChannelsJSON, " ") event.NotifyGroups = strings.Join(event.NotifyGroupsJSON, " ") - promstat.CounterAlertsTotal.WithLabelValues(event.Cluster).Inc() + rt.AlertStats.CounterAlertsTotal.WithLabelValues(event.Cluster).Inc() - engine.LogEvent(event, "http_push_queue") - if !engine.EventQueue.PushFront(event) { + dispatch.LogEvent(event, "http_push_queue") + if !queue.EventQueue.PushFront(event) { msg := fmt.Sprintf("event:%+v push_queue err: queue is full", event) ginx.Bomb(200, msg) logger.Warningf(msg) @@ -79,35 +84,25 @@ func pushEventToQueue(c *gin.Context) { } type eventForm struct { - Alert bool `json:"alert"` - Vectors []conv.Vector `json:"vectors"` - RuleId int64 `json:"rule_id"` - Cluster string `json:"cluster"` -} - -func judgeEvent(c *gin.Context) { - var form eventForm - ginx.BindJSON(c, &form) - ruleContext, exists := engine.GetExternalAlertRule(form.Cluster, form.RuleId) - if !exists { - ginx.Bomb(200, "rule not exists") - } - ruleContext.HandleVectors(form.Vectors, "http") - ginx.NewRender(c).Message(nil) + Alert bool `json:"alert"` + AnomalyPoints []common.AnomalyPoint `json:"vectors"` + RuleId int64 `json:"rule_id"` + DatasourceId int64 `json:"datasource_id"` + Inhibit bool `json:"inhibit"` } -func makeEvent(c *gin.Context) { +func (rt *Router) makeEvent(c *gin.Context) { var events []*eventForm ginx.BindJSON(c, &events) //now := time.Now().Unix() for i := 0; i < len(events); i++ { - node, err := naming.ClusterHashRing.GetNode(events[i].Cluster, fmt.Sprintf("%d", events[i].RuleId)) + node, err := naming.DatasourceHashRing.GetNode(events[i].DatasourceId, fmt.Sprintf("%d", events[i].RuleId)) if err != nil { logger.Warningf("event:%+v get node err:%v", events[i], err) ginx.Bomb(200, "event node not exists") } - if node != config.C.Heartbeat.Endpoint { + if node != rt.Alert.Heartbeat.Endpoint { err := forwardEvent(events[i], node) if err != nil { logger.Warningf("event:%+v forward err:%v", events[i], err) @@ -116,19 +111,18 @@ func makeEvent(c *gin.Context) { continue } - ruleContext, exists := engine.GetExternalAlertRule(events[i].Cluster, events[i].RuleId) + ruleWorker, exists := rt.ExternalProcessors.GetExternalAlertRule(events[i].DatasourceId, events[i].RuleId) logger.Debugf("handle event:%+v exists:%v", events[i], exists) if !exists { ginx.Bomb(200, "rule not exists") } if events[i].Alert { - go ruleContext.HandleVectors(events[i].Vectors, "http") + go ruleWorker.Handle(events[i].AnomalyPoints, "http", events[i].Inhibit) } else { - for _, vector := range events[i].Vectors { - alertVector := engine.NewAlertVector(ruleContext, nil, vector, "http") + for _, vector := range events[i].AnomalyPoints { readableString := vector.ReadableValue() - go ruleContext.RecoverSingle(alertVector.Hash(), vector.Timestamp, &readableString) + go ruleWorker.RecoverSingle(process.Hash(events[i].RuleId, events[i].DatasourceId, vector), vector.Timestamp, &readableString) } } } diff --git a/src/server/common/sender/callback.go b/alert/sender/callback.go similarity index 74% rename from src/server/common/sender/callback.go rename to alert/sender/callback.go index 6a181aceb31d2b94136b9ed21d418a3dd5c6e4cc..e8ba9979eb6354918b6ae466b034a5f039861bda 100644 --- a/src/server/common/sender/callback.go +++ b/alert/sender/callback.go @@ -5,16 +5,17 @@ import ( "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/ibex" + "github.com/ccfos/nightingale/v6/pkg/poster" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/ibex" - "github.com/didi/nightingale/v5/src/pkg/poster" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" + "github.com/toolkits/pkg/logger" ) -func SendCallbacks(urls []string, event *models.AlertCurEvent) { +func SendCallbacks(ctx *ctx.Context, urls []string, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType, ibexConf aconf.Ibex) { for _, url := range urls { if url == "" { continue @@ -22,7 +23,7 @@ func SendCallbacks(urls []string, event *models.AlertCurEvent) { if strings.HasPrefix(url, "${ibex}") { if !event.IsRecovered { - handleIbex(url, event) + handleIbex(ctx, url, event, targetCache, ibexConf) } continue } @@ -59,7 +60,7 @@ type TaskCreateReply struct { Dat int64 `json:"dat"` // task.id } -func handleIbex(url string, event *models.AlertCurEvent) { +func handleIbex(ctx *ctx.Context, url string, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType, ibexConf aconf.Ibex) { arr := strings.Split(url, "/") var idstr string @@ -89,7 +90,7 @@ func handleIbex(url string, event *models.AlertCurEvent) { return } - tpl, err := models.TaskTplGet("id = ?", id) + tpl, err := models.TaskTplGet(ctx, "id = ?", id) if err != nil { logger.Errorf("event_callback_ibex: failed to get tpl: %v", err) return @@ -102,7 +103,7 @@ func handleIbex(url string, event *models.AlertCurEvent) { // check perm // tpl.GroupId - host - account 三元组校验权限 - can, err := canDoIbex(tpl.UpdateBy, tpl, host) + can, err := canDoIbex(ctx, tpl.UpdateBy, tpl, host, targetCache) if err != nil { logger.Errorf("event_callback_ibex: check perm fail: %v", err) return @@ -130,10 +131,10 @@ func handleIbex(url string, event *models.AlertCurEvent) { var res TaskCreateReply err = ibex.New( - config.C.Ibex.Address, - config.C.Ibex.BasicAuthUser, - config.C.Ibex.BasicAuthPass, - config.C.Ibex.Timeout, + ibexConf.Address, + ibexConf.BasicAuthUser, + ibexConf.BasicAuthPass, + ibexConf.Timeout, ). Path("/ibex/v1/tasks"). In(in). @@ -154,9 +155,9 @@ func handleIbex(url string, event *models.AlertCurEvent) { record := models.TaskRecord{ Id: res.Dat, GroupId: tpl.GroupId, - IbexAddress: config.C.Ibex.Address, - IbexAuthUser: config.C.Ibex.BasicAuthUser, - IbexAuthPass: config.C.Ibex.BasicAuthPass, + IbexAddress: ibexConf.Address, + IbexAuthUser: ibexConf.BasicAuthUser, + IbexAuthPass: ibexConf.BasicAuthPass, Title: in.Title, Account: in.Account, Batch: in.Batch, @@ -169,13 +170,13 @@ func handleIbex(url string, event *models.AlertCurEvent) { CreateBy: in.Creator, } - if err = record.Add(); err != nil { + if err = record.Add(ctx); err != nil { logger.Errorf("event_callback_ibex: persist task_record fail: %v", err) } } -func canDoIbex(username string, tpl *models.TaskTpl, host string) (bool, error) { - user, err := models.UserGetByUsername(username) +func canDoIbex(ctx *ctx.Context, username string, tpl *models.TaskTpl, host string, targetCache *memsto.TargetCacheType) (bool, error) { + user, err := models.UserGetByUsername(ctx, username) if err != nil { return false, err } @@ -184,7 +185,7 @@ func canDoIbex(username string, tpl *models.TaskTpl, host string) (bool, error) return true, nil } - target, has := memsto.TargetCache.Get(host) + target, has := targetCache.Get(host) if !has { return false, nil } diff --git a/src/server/common/sender/dingtalk.go b/alert/sender/dingtalk.go similarity index 84% rename from src/server/common/sender/dingtalk.go rename to alert/sender/dingtalk.go index ccd60bd9f2cad04af43a06e7d9453ef865ae028d..694a902131241a2ad7ca71c8d8186054b82a3a2a 100644 --- a/src/server/common/sender/dingtalk.go +++ b/alert/sender/dingtalk.go @@ -5,10 +5,10 @@ import ( "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/poster" + "github.com/toolkits/pkg/logger" ) type dingtalkMarkdown struct { @@ -70,23 +70,6 @@ func (ds *DingtalkSender) Send(ctx MessageContext) { } } -func (ds *DingtalkSender) SendRaw(users []*models.User, title, message string) { - if len(users) == 0 { - return - } - urls, _ := ds.extract(users) - body := dingtalk{ - Msgtype: "markdown", - Markdown: dingtalkMarkdown{ - Title: title, - Text: message, - }, - } - for _, url := range urls { - ds.doSend(url, body) - } -} - // extract urls and ats from Users func (ds *DingtalkSender) extract(users []*models.User) ([]string, []string) { urls := make([]string, 0, len(users)) diff --git a/src/server/common/sender/email.go b/alert/sender/email.go similarity index 82% rename from src/server/common/sender/email.go rename to alert/sender/email.go index 7e8465906c03ab7e51a10ba29ff22eceffd364db..67606d4c3eb7129b9bbaa1a4ba8090a283d25e54 100644 --- a/src/server/common/sender/email.go +++ b/alert/sender/email.go @@ -5,11 +5,12 @@ import ( "html/template" "time" + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/models" + "github.com/toolkits/pkg/logger" - "gopkg.in/gomail.v2" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" + "gopkg.in/gomail.v2" ) var mailch chan *gomail.Message @@ -17,13 +18,14 @@ var mailch chan *gomail.Message type EmailSender struct { subjectTpl *template.Template contentTpl *template.Template + smtp aconf.SMTPConfig } func (es *EmailSender) Send(ctx MessageContext) { if len(ctx.Users) == 0 || ctx.Rule == nil || ctx.Event == nil { return } - tos := es.extract(ctx.Users) + tos := extract(ctx.Users) var subject string if es.subjectTpl != nil { @@ -32,15 +34,10 @@ func (es *EmailSender) Send(ctx MessageContext) { subject = ctx.Rule.Name } content := BuildTplMessage(es.contentTpl, ctx.Event) - WriteEmail(subject, content, tos) -} - -func (es *EmailSender) SendRaw(users []*models.User, title, message string) { - tos := es.extract(users) - WriteEmail(title, message, tos) + es.WriteEmail(subject, content, tos) } -func (es *EmailSender) extract(users []*models.User) []string { +func extract(users []*models.User) []string { tos := make([]string, 0, len(users)) for _, u := range users { if u.Email != "" { @@ -50,8 +47,8 @@ func (es *EmailSender) extract(users []*models.User) []string { return tos } -func SendEmail(subject, content string, tos []string) { - conf := config.C.SMTP +func (es *EmailSender) SendEmail(subject, content string, tos []string, stmp aconf.SMTPConfig) { + conf := stmp d := gomail.NewDialer(conf.Host, conf.Port, conf.User, conf.Pass) if conf.InsecureSkipVerify { @@ -60,7 +57,7 @@ func SendEmail(subject, content string, tos []string) { m := gomail.NewMessage() - m.SetHeader("From", config.C.SMTP.From) + m.SetHeader("From", stmp.From) m.SetHeader("To", tos...) m.SetHeader("Subject", subject) m.SetBody("text/html", content) @@ -71,10 +68,10 @@ func SendEmail(subject, content string, tos []string) { } } -func WriteEmail(subject, content string, tos []string) { +func (es *EmailSender) WriteEmail(subject, content string, tos []string) { m := gomail.NewMessage() - m.SetHeader("From", config.C.SMTP.From) + m.SetHeader("From", es.smtp.From) m.SetHeader("To", tos...) m.SetHeader("Subject", subject) m.SetBody("text/html", content) @@ -94,10 +91,10 @@ func dialSmtp(d *gomail.Dialer) gomail.SendCloser { } } -func StartEmailSender() { +func StartEmailSender(smtp aconf.SMTPConfig) { mailch = make(chan *gomail.Message, 100000) - conf := config.C.SMTP + conf := smtp if conf.Host == "" || conf.Port == 0 { logger.Warning("SMTP configurations invalid") diff --git a/src/server/common/sender/feishu.go b/alert/sender/feishu.go similarity index 82% rename from src/server/common/sender/feishu.go rename to alert/sender/feishu.go index 3d994885078efd90db5b44e9ba412b266c7bed95..bf03176dfcde8e53c8e47ad65546061fd499753a 100644 --- a/src/server/common/sender/feishu.go +++ b/alert/sender/feishu.go @@ -5,10 +5,10 @@ import ( "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/poster" + "github.com/toolkits/pkg/logger" ) type feishuContent struct { @@ -53,22 +53,6 @@ func (fs *FeishuSender) Send(ctx MessageContext) { } } -func (fs *FeishuSender) SendRaw(users []*models.User, title, message string) { - if len(users) == 0 { - return - } - urls, _ := fs.extract(users) - body := feishu{ - Msgtype: "text", - Content: feishuContent{ - Text: message, - }, - } - for _, url := range urls { - fs.doSend(url, body) - } -} - func (fs *FeishuSender) extract(users []*models.User) ([]string, []string) { urls := make([]string, 0, len(users)) ats := make([]string, 0, len(users)) diff --git a/src/server/common/sender/mm.go b/alert/sender/mm.go similarity index 88% rename from src/server/common/sender/mm.go rename to alert/sender/mm.go index 3fc94c8396e619dfd5345d6638555efaa8f4d4c9..d61ecc15f0652a90d68711817a881df33a7fe549 100644 --- a/src/server/common/sender/mm.go +++ b/alert/sender/mm.go @@ -6,10 +6,10 @@ import ( "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/poster" + "github.com/toolkits/pkg/logger" ) type MatterMostMessage struct { @@ -44,17 +44,6 @@ func (ms *MmSender) Send(ctx MessageContext) { }) } -func (ms *MmSender) SendRaw(users []*models.User, title, message string) { - urls := ms.extract(users) - if len(urls) == 0 { - return - } - SendMM(MatterMostMessage{ - Text: message, - Tokens: urls, - }) -} - func (ms *MmSender) extract(users []*models.User) []string { tokens := make([]string, 0, len(users)) for _, user := range users { diff --git a/alert/sender/plugin.go b/alert/sender/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..d3e51208918a1c87de46296e6c83bf7962a4f0ac --- /dev/null +++ b/alert/sender/plugin.go @@ -0,0 +1,97 @@ +package sender + +import ( + "bytes" + "os" + "os/exec" + "time" + + "github.com/ccfos/nightingale/v6/memsto" + + "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/sys" +) + +func MayPluginNotify(noticeBytes []byte, notifyScript *memsto.NotifyScriptCacheType) { + if len(noticeBytes) == 0 { + return + } + alertingCallScript(noticeBytes, notifyScript) +} + +func alertingCallScript(stdinBytes []byte, notifyScript *memsto.NotifyScriptCacheType) { + // not enable or no notify.py? do nothing + config := notifyScript.GetNotifyScript() + if !config.Enable || config.Content == "" { + return + } + + fpath := ".notify_scriptt" + if config.Type == 1 { + fpath = config.Content + } else { + rewrite := true + if file.IsExist(fpath) { + oldContent, err := file.ToString(fpath) + if err != nil { + logger.Errorf("event_notify: read script file err: %v", err) + return + } + + if oldContent == config.Content { + rewrite = false + } + } + + if rewrite { + _, err := file.WriteString(fpath, config.Content) + if err != nil { + logger.Errorf("event_notify: write script file err: %v", err) + return + } + + err = os.Chmod(fpath, 0777) + if err != nil { + logger.Errorf("event_notify: chmod script file err: %v", err) + return + } + } + fpath = "./" + fpath + } + + cmd := exec.Command(fpath) + cmd.Stdin = bytes.NewReader(stdinBytes) + + // combine stdout and stderr + var buf bytes.Buffer + cmd.Stdout = &buf + cmd.Stderr = &buf + + err := startCmd(cmd) + if err != nil { + logger.Errorf("event_notify: run cmd err: %v", err) + return + } + + err, isTimeout := sys.WrapTimeout(cmd, time.Duration(config.Timeout)*time.Second) + + if isTimeout { + if err == nil { + logger.Errorf("event_notify: timeout and killed process %s", fpath) + } + + if err != nil { + logger.Errorf("event_notify: kill process %s occur error %v", fpath, err) + } + + return + } + + if err != nil { + logger.Errorf("event_notify: exec script %s occur error: %v, output: %s", fpath, err, buf.String()) + return + } + + logger.Infof("event_notify: exec %s output: %s", fpath, buf.String()) +} diff --git a/src/server/common/sender/plugin_cmd_unix.go b/alert/sender/plugin_cmd_unix.go similarity index 100% rename from src/server/common/sender/plugin_cmd_unix.go rename to alert/sender/plugin_cmd_unix.go diff --git a/src/server/common/sender/plugin_cmd_windows.go b/alert/sender/plugin_cmd_windows.go similarity index 100% rename from src/server/common/sender/plugin_cmd_windows.go rename to alert/sender/plugin_cmd_windows.go diff --git a/src/server/common/sender/sender.go b/alert/sender/sender.go similarity index 54% rename from src/server/common/sender/sender.go rename to alert/sender/sender.go index e1d5c28ea7e2b6f17f3123b5c5cab7ac6f4d2bdc..a78adf9c2ed9d049b3a296511b2c36a7a8fb5bfc 100644 --- a/src/server/common/sender/sender.go +++ b/alert/sender/sender.go @@ -4,20 +4,14 @@ import ( "bytes" "html/template" - "github.com/toolkits/pkg/slice" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" ) type ( // Sender 发送消息通知的接口 Sender interface { Send(ctx MessageContext) - - // SendRaw 发送原始消息,目前在notifyMaintainer时使用 - SendRaw(users []*models.User, title, message string) } // MessageContext 一个event所生成的告警通知的上下文 @@ -29,29 +23,25 @@ type ( ) func NewSender(key string, tpls map[string]*template.Template) Sender { - if !slice.ContainsString(config.C.Alerting.NotifyBuiltinChannels, key) { - return nil - } - switch key { case models.Dingtalk: - return &DingtalkSender{tpl: tpls["dingtalk.tpl"]} + return &DingtalkSender{tpl: tpls[models.Dingtalk]} case models.Wecom: - return &WecomSender{tpl: tpls["wecom.tpl"]} + return &WecomSender{tpl: tpls[models.Wecom]} case models.Feishu: - return &FeishuSender{tpl: tpls["feishu.tpl"]} + return &FeishuSender{tpl: tpls[models.Feishu]} case models.Email: - return &EmailSender{subjectTpl: tpls["subject.tpl"], contentTpl: tpls["mailbody.tpl"]} + return &EmailSender{subjectTpl: tpls["mailsubject"], contentTpl: tpls[models.Email]} case models.Mm: - return &MmSender{tpl: tpls["mm.tpl"]} + return &MmSender{tpl: tpls[models.Mm]} case models.Telegram: - return &TelegramSender{tpl: tpls["telegram.tpl"]} + return &TelegramSender{tpl: tpls[models.Telegram]} } return nil } -func BuildMessageContext(rule *models.AlertRule, event *models.AlertCurEvent, uids []int64) MessageContext { - users := memsto.UserCache.GetByUserIds(uids) +func BuildMessageContext(rule *models.AlertRule, event *models.AlertCurEvent, uids []int64, userCache *memsto.UserCacheType) MessageContext { + users := userCache.GetByUserIds(uids) return MessageContext{ Rule: rule, Event: event, diff --git a/src/server/common/sender/telegram.go b/alert/sender/telegram.go similarity index 87% rename from src/server/common/sender/telegram.go rename to alert/sender/telegram.go index 8d7aee4dddfc9d70f3f03110e25b61c7975bfccd..75ded37ad11342d383015cac17b5d3087848a906 100644 --- a/src/server/common/sender/telegram.go +++ b/alert/sender/telegram.go @@ -5,10 +5,10 @@ import ( "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/poster" + "github.com/toolkits/pkg/logger" ) type TelegramMessage struct { @@ -38,14 +38,6 @@ func (ts *TelegramSender) Send(ctx MessageContext) { }) } -func (ts *TelegramSender) SendRaw(users []*models.User, title, message string) { - tokens := ts.extract(users) - SendTelegram(TelegramMessage{ - Text: message, - Tokens: tokens, - }) -} - func (ts *TelegramSender) extract(users []*models.User) []string { tokens := make([]string, 0, len(users)) for _, user := range users { diff --git a/src/server/common/sender/webhook.go b/alert/sender/webhook.go similarity index 86% rename from src/server/common/sender/webhook.go rename to alert/sender/webhook.go index 43e64b572a52d3315e010c0df26c092d9f6769ac..c8fb552b8278fdede2ee265496d475820cc2d8be 100644 --- a/src/server/common/sender/webhook.go +++ b/alert/sender/webhook.go @@ -5,14 +5,14 @@ import ( "encoding/json" "io/ioutil" "net/http" + "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" + "github.com/toolkits/pkg/logger" ) -func SendWebhooks(webhooks []config.Webhook, event *models.AlertCurEvent) { +func SendWebhooks(webhooks []*models.Webhook, event *models.AlertCurEvent) { for _, conf := range webhooks { if conf.Url == "" || !conf.Enable { continue @@ -44,8 +44,9 @@ func SendWebhooks(webhooks []config.Webhook, event *models.AlertCurEvent) { } } + // todo add skip verify client := http.Client{ - Timeout: conf.TimeoutDuration, + Timeout: time.Duration(conf.Timeout) * time.Second, } var resp *http.Response diff --git a/src/server/common/sender/wecom.go b/alert/sender/wecom.go similarity index 79% rename from src/server/common/sender/wecom.go rename to alert/sender/wecom.go index 3ed58a287e7e892fa81b341df33ba3e87076b5c7..6044eff8786959fe37fcbac413dc17b236258ffe 100644 --- a/src/server/common/sender/wecom.go +++ b/alert/sender/wecom.go @@ -5,10 +5,10 @@ import ( "strings" "time" - "github.com/toolkits/pkg/logger" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/poster" - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/poster" + "github.com/toolkits/pkg/logger" ) type wecomMarkdown struct { @@ -41,19 +41,6 @@ func (ws *WecomSender) Send(ctx MessageContext) { } } -func (ws *WecomSender) SendRaw(users []*models.User, title, message string) { - urls := ws.extract(users) - for _, url := range urls { - body := wecom{ - Msgtype: "markdown", - Markdown: wecomMarkdown{ - Content: message, - }, - } - ws.doSend(url, body) - } -} - func (ws *WecomSender) extract(users []*models.User) []string { urls := make([]string, 0, len(users)) for _, user := range users { diff --git a/center/cconf/conf.go b/center/cconf/conf.go new file mode 100644 index 0000000000000000000000000000000000000000..b71c67bf605a8adb2932dd177c98e2af4bc6cc18 --- /dev/null +++ b/center/cconf/conf.go @@ -0,0 +1,67 @@ +package cconf + +import ( + "github.com/ccfos/nightingale/v6/pkg/cas" + "github.com/ccfos/nightingale/v6/pkg/ldapx" + "github.com/ccfos/nightingale/v6/pkg/oauth2x" + "github.com/ccfos/nightingale/v6/pkg/oidcx" + + "github.com/gin-gonic/gin" +) + +type Center struct { + Plugins []Plugin + BasicAuth gin.Accounts + MetricsYamlFile string + OpsYamlFile string + BuiltinIntegrationsDir string + I18NHeaderKey string + MetricDesc MetricDescType + TargetMetrics map[string]string + AnonymousAccess AnonymousAccess + JWTAuth JWTAuth + ProxyAuth ProxyAuth + LDAP ldapx.Config + OIDC oidcx.Config + CAS cas.Config + OAuth oauth2x.Config + Ibex Ibex +} + +type Plugin struct { + Id int64 `json:"id"` + Category string `json:"category"` + Type string `json:"plugin_type"` + TypeName string `json:"plugin_type_name"` +} + +type ProxyAuth struct { + Enable bool + HeaderUserNameKey string + DefaultRoles []string +} + +type JWTAuth struct { + SigningKey string + AccessExpired int64 + RefreshExpired int64 + RedisKeyPrefix string +} + +type AnonymousAccess struct { + PromQuerier bool + AlertDetail bool +} + +type Ibex struct { + Address string + BasicAuthUser string + BasicAuthPass string + Timeout int64 +} + +func (c *Center) PreCheck() { + if len(c.Plugins) == 0 { + c.Plugins = Plugins + } +} diff --git a/src/webapi/config/metrics.go b/center/cconf/metric.go similarity index 85% rename from src/webapi/config/metrics.go rename to center/cconf/metric.go index cf18fbb14e8ac47194b7e8578d6f4c4859a9f117..a8b12f8544e0574936c22d4c53197ffd20a85377 100644 --- a/src/webapi/config/metrics.go +++ b/center/cconf/metric.go @@ -1,4 +1,4 @@ -package config +package cconf import ( "path" @@ -8,13 +8,13 @@ import ( ) // metricDesc , As load map happens before read map, there is no necessary to use concurrent map for metric desc store -type metricDesc struct { +type MetricDescType struct { CommonDesc map[string]string `yaml:",inline" json:"common"` Zh map[string]string `yaml:"zh" json:"zh"` En map[string]string `yaml:"en" json:"en"` } -var MetricDesc metricDesc +var MetricDesc MetricDescType // GetMetricDesc , if metric is not registered, empty string will be returned func GetMetricDesc(lang, metric string) string { @@ -33,8 +33,8 @@ func GetMetricDesc(lang, metric string) string { return MetricDesc.CommonDesc[metric] } -func loadMetricsYaml() error { - fp := C.MetricsYamlFile +func LoadMetricsYaml(metricsYamlFile string) error { + fp := metricsYamlFile if fp == "" { fp = path.Join(runner.Cwd, "etc", "metrics.yaml") } diff --git a/center/cconf/ops.go b/center/cconf/ops.go new file mode 100644 index 0000000000000000000000000000000000000000..4489a64e745e78c7ab6e29581f5b88961bdbe9ea --- /dev/null +++ b/center/cconf/ops.go @@ -0,0 +1,39 @@ +package cconf + +import ( + "path" + + "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/runner" +) + +var Operations = Operation{} + +type Operation struct { + Ops []Ops `yaml:"ops"` +} + +type Ops struct { + Name string `yaml:"name" json:"name"` + Cname string `yaml:"cname" json:"cname"` + Ops []string `yaml:"ops" json:"ops"` +} + +func LoadOpsYaml(opsYamlFile string) error { + fp := opsYamlFile + if fp == "" { + fp = path.Join(runner.Cwd, "etc", "ops.yaml") + } + if !file.IsExist(fp) { + return nil + } + return file.ReadYaml(fp, &Operations) +} + +func GetAllOps(ops []Ops) []string { + var ret []string + for _, op := range ops { + ret = append(ret, op.Ops...) + } + return ret +} diff --git a/center/cconf/plugin.go b/center/cconf/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..3a37f7829f16ebf69f93cd90b0dcb3dea9f7ab5a --- /dev/null +++ b/center/cconf/plugin.go @@ -0,0 +1,22 @@ +package cconf + +var Plugins = []Plugin{ + { + Id: 1, + Category: "timeseries", + Type: "prometheus", + TypeName: "Prometheus Like", + }, + { + Id: 2, + Category: "logging", + Type: "elasticsearch", + TypeName: "Elasticsearch", + }, + { + Id: 3, + Category: "logging", + Type: "jaeger", + TypeName: "Jaeger", + }, +} diff --git a/center/center.go b/center/center.go new file mode 100644 index 0000000000000000000000000000000000000000..76ff257f1f5cb41a2b43add0b542dad5da4a6e13 --- /dev/null +++ b/center/center.go @@ -0,0 +1,92 @@ +package center + +import ( + "context" + "fmt" + + "github.com/ccfos/nightingale/v6/alert" + "github.com/ccfos/nightingale/v6/alert/astats" + "github.com/ccfos/nightingale/v6/alert/process" + "github.com/ccfos/nightingale/v6/center/cconf" + "github.com/ccfos/nightingale/v6/center/sso" + "github.com/ccfos/nightingale/v6/conf" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/httpx" + "github.com/ccfos/nightingale/v6/pkg/logx" + "github.com/ccfos/nightingale/v6/prom" + "github.com/ccfos/nightingale/v6/pushgw/idents" + "github.com/ccfos/nightingale/v6/pushgw/writer" + "github.com/ccfos/nightingale/v6/storage" + + alertrt "github.com/ccfos/nightingale/v6/alert/router" + centerrt "github.com/ccfos/nightingale/v6/center/router" + pushgwrt "github.com/ccfos/nightingale/v6/pushgw/router" + + "github.com/toolkits/pkg/i18n" +) + +func Initialize(configDir string, cryptoKey string) (func(), error) { + config, err := conf.InitConfig(configDir, cryptoKey) + if err != nil { + return nil, fmt.Errorf("failed to init config: %v", err) + } + + cconf.LoadMetricsYaml(config.Center.MetricsYamlFile) + cconf.LoadOpsYaml(config.Center.OpsYamlFile) + + logxClean, err := logx.Init(config.Log) + if err != nil { + return nil, err + } + + i18n.Init() + + db, err := storage.New(config.DB) + if err != nil { + return nil, err + } + ctx := ctx.NewContext(context.Background(), db) + models.InitRoot(ctx) + + redis, err := storage.NewRedis(config.Redis) + if err != nil { + return nil, err + } + + syncStats := memsto.NewSyncStats() + alertStats := astats.NewSyncStats() + idents := idents.New(db, config.Pushgw.DatasourceId, config.Pushgw.MaxOffset) + sso := sso.Init(config.Center, ctx) + + busiGroupCache := memsto.NewBusiGroupCache(ctx, syncStats) + targetCache := memsto.NewTargetCache(ctx, syncStats) + dsCache := memsto.NewDatasourceCache(ctx, syncStats) + alertMuteCache := memsto.NewAlertMuteCache(ctx, syncStats) + alertRuleCache := memsto.NewAlertRuleCache(ctx, syncStats) + + promClients := prom.NewPromClient(ctx, config.Alert.Heartbeat) + + externalProcessors := process.NewExternalProcessors() + alert.Start(config.Alert, config.Pushgw, syncStats, alertStats, externalProcessors, targetCache, busiGroupCache, alertMuteCache, alertRuleCache, ctx, promClients) + + writers := writer.NewWriters(config.Pushgw) + + alertrtRouter := alertrt.New(config.HTTP, config.Alert, alertMuteCache, targetCache, busiGroupCache, alertStats, ctx, externalProcessors) + centerRouter := centerrt.New(config.HTTP, config.Center, cconf.Operations, dsCache, promClients, redis, sso, ctx) + pushgwRouter := pushgwrt.New(config.HTTP, config.Pushgw, targetCache, busiGroupCache, idents, writers, ctx) + + r := httpx.GinEngine(config.Global.RunMode, config.HTTP) + + centerRouter.Config(r) + alertrtRouter.Config(r) + pushgwRouter.Config(r) + + httpClean := httpx.Init(config.HTTP, r) + + return func() { + logxClean() + httpClean() + }, nil +} diff --git a/src/webapi/stat/stat.go b/center/cstats/stats.go similarity index 95% rename from src/webapi/stat/stat.go rename to center/cstats/stats.go index 52d539c6ecb876f8df78e86114277dcd7d7e61ff..07e3705112785400b234d21a54d22d09f54b7285 100644 --- a/src/webapi/stat/stat.go +++ b/center/cstats/stats.go @@ -1,4 +1,4 @@ -package stat +package cstats import ( "time" @@ -6,7 +6,7 @@ import ( "github.com/prometheus/client_golang/prometheus" ) -const Service = "n9e-webapi" +const Service = "n9e-center" var ( labels = []string{"service", "code", "path", "method"} diff --git a/center/router/router.go b/center/router/router.go new file mode 100644 index 0000000000000000000000000000000000000000..b553c8a074bd601f18dc163efc234aad312f36fe --- /dev/null +++ b/center/router/router.go @@ -0,0 +1,374 @@ +package router + +import ( + "fmt" + "net/http" + "path" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/center/cconf" + "github.com/ccfos/nightingale/v6/center/cstats" + "github.com/ccfos/nightingale/v6/center/sso" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/pkg/aop" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/httpx" + "github.com/ccfos/nightingale/v6/prom" + "github.com/ccfos/nightingale/v6/storage" + "github.com/toolkits/pkg/ginx" + + "github.com/gin-gonic/gin" +) + +type Router struct { + HTTP httpx.Config + Center cconf.Center + Operations cconf.Operation + DatasourceCache *memsto.DatasourceCacheType + PromClients *prom.PromClientMap + Redis storage.Redis + Sso *sso.SsoClient + Ctx *ctx.Context +} + +func New(httpConfig httpx.Config, center cconf.Center, operations cconf.Operation, ds *memsto.DatasourceCacheType, pc *prom.PromClientMap, + redis storage.Redis, sso *sso.SsoClient, ctx *ctx.Context) *Router { + return &Router{ + HTTP: httpConfig, + Center: center, + Operations: operations, + DatasourceCache: ds, + PromClients: pc, + Redis: redis, + Sso: sso, + Ctx: ctx, + } +} + +func stat() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + c.Next() + + code := fmt.Sprintf("%d", c.Writer.Status()) + method := c.Request.Method + labels := []string{cstats.Service, code, c.FullPath(), method} + + cstats.RequestCounter.WithLabelValues(labels...).Inc() + cstats.RequestDuration.WithLabelValues(labels...).Observe(float64(time.Since(start).Seconds())) + } +} + +func languageDetector(i18NHeaderKey string) gin.HandlerFunc { + headerKey := i18NHeaderKey + return func(c *gin.Context) { + if headerKey != "" { + lang := c.GetHeader(headerKey) + if lang != "" { + if strings.HasPrefix(lang, "zh") { + c.Request.Header.Set("X-Language", "zh") + } else if strings.HasPrefix(lang, "en") { + c.Request.Header.Set("X-Language", "en") + } else { + c.Request.Header.Set("X-Language", lang) + } + } else { + c.Request.Header.Set("X-Language", "en") + } + } + c.Next() + } +} + +func (rt *Router) configNoRoute(r *gin.Engine) { + r.NoRoute(func(c *gin.Context) { + arr := strings.Split(c.Request.URL.Path, ".") + suffix := arr[len(arr)-1] + switch suffix { + case "png", "jpeg", "jpg", "svg", "ico", "gif", "css", "js", "html", "htm", "gz", "zip", "map": + c.File(path.Join(strings.Split("pub/"+c.Request.URL.Path, "/")...)) + default: + c.File(path.Join("pub", "index.html")) + } + }) +} + +func (rt *Router) Config(r *gin.Engine) { + r.Use(stat()) + r.Use(languageDetector(rt.Center.I18NHeaderKey)) + r.Use(aop.Recovery()) + + pagesPrefix := "/api/n9e" + pages := r.Group(pagesPrefix) + { + + if rt.Center.AnonymousAccess.PromQuerier { + pages.Any("/proxy/:id/*url", rt.dsProxy) + pages.POST("/query-range-batch", rt.promBatchQueryRange) + } else { + pages.Any("/proxy/:id/*url", rt.auth(), rt.dsProxy) + pages.POST("/query-range-batch", rt.auth(), rt.promBatchQueryRange) + } + + pages.POST("/auth/login", rt.jwtMock(), rt.loginPost) + pages.POST("/auth/logout", rt.jwtMock(), rt.logoutPost) + pages.POST("/auth/refresh", rt.jwtMock(), rt.refreshPost) + + pages.GET("/auth/sso-config", rt.ssoConfigNameGet) + pages.GET("/auth/redirect", rt.loginRedirect) + pages.GET("/auth/redirect/cas", rt.loginRedirectCas) + pages.GET("/auth/redirect/oauth", rt.loginRedirectOAuth) + pages.GET("/auth/callback", rt.loginCallback) + pages.GET("/auth/callback/cas", rt.loginCallbackCas) + pages.GET("/auth/callback/oauth", rt.loginCallbackOAuth) + + pages.GET("/metrics/desc", rt.metricsDescGetFile) + pages.POST("/metrics/desc", rt.metricsDescGetMap) + + pages.GET("/notify-channels", rt.notifyChannelsGets) + pages.GET("/contact-keys", rt.contactKeysGets) + + pages.GET("/self/perms", rt.auth(), rt.user(), rt.permsGets) + pages.GET("/self/profile", rt.auth(), rt.user(), rt.selfProfileGet) + pages.PUT("/self/profile", rt.auth(), rt.user(), rt.selfProfilePut) + pages.PUT("/self/password", rt.auth(), rt.user(), rt.selfPasswordPut) + + pages.GET("/users", rt.auth(), rt.user(), rt.perm("/users"), rt.userGets) + pages.POST("/users", rt.auth(), rt.admin(), rt.userAddPost) + pages.GET("/user/:id/profile", rt.auth(), rt.userProfileGet) + pages.PUT("/user/:id/profile", rt.auth(), rt.admin(), rt.userProfilePut) + pages.PUT("/user/:id/password", rt.auth(), rt.admin(), rt.userPasswordPut) + pages.DELETE("/user/:id", rt.auth(), rt.admin(), rt.userDel) + + pages.GET("/metric-views", rt.auth(), rt.metricViewGets) + pages.DELETE("/metric-views", rt.auth(), rt.user(), rt.metricViewDel) + pages.POST("/metric-views", rt.auth(), rt.user(), rt.metricViewAdd) + pages.PUT("/metric-views", rt.auth(), rt.user(), rt.metricViewPut) + + pages.GET("/user-groups", rt.auth(), rt.user(), rt.userGroupGets) + pages.POST("/user-groups", rt.auth(), rt.user(), rt.perm("/user-groups/add"), rt.userGroupAdd) + pages.GET("/user-group/:id", rt.auth(), rt.user(), rt.userGroupGet) + pages.PUT("/user-group/:id", rt.auth(), rt.user(), rt.perm("/user-groups/put"), rt.userGroupWrite(), rt.userGroupPut) + pages.DELETE("/user-group/:id", rt.auth(), rt.user(), rt.perm("/user-groups/del"), rt.userGroupWrite(), rt.userGroupDel) + pages.POST("/user-group/:id/members", rt.auth(), rt.user(), rt.perm("/user-groups/put"), rt.userGroupWrite(), rt.userGroupMemberAdd) + pages.DELETE("/user-group/:id/members", rt.auth(), rt.user(), rt.perm("/user-groups/put"), rt.userGroupWrite(), rt.userGroupMemberDel) + + pages.GET("/busi-groups", rt.auth(), rt.user(), rt.busiGroupGets) + pages.POST("/busi-groups", rt.auth(), rt.user(), rt.perm("/busi-groups/add"), rt.busiGroupAdd) + pages.GET("/busi-groups/alertings", rt.auth(), rt.busiGroupAlertingsGets) + pages.GET("/busi-group/:id", rt.auth(), rt.user(), rt.bgro(), rt.busiGroupGet) + pages.PUT("/busi-group/:id", rt.auth(), rt.user(), rt.perm("/busi-groups/put"), rt.bgrw(), rt.busiGroupPut) + pages.POST("/busi-group/:id/members", rt.auth(), rt.user(), rt.perm("/busi-groups/put"), rt.bgrw(), rt.busiGroupMemberAdd) + pages.DELETE("/busi-group/:id/members", rt.auth(), rt.user(), rt.perm("/busi-groups/put"), rt.bgrw(), rt.busiGroupMemberDel) + pages.DELETE("/busi-group/:id", rt.auth(), rt.user(), rt.perm("/busi-groups/del"), rt.bgrw(), rt.busiGroupDel) + pages.GET("/busi-group/:id/perm/:perm", rt.auth(), rt.user(), rt.checkBusiGroupPerm) + + pages.GET("/targets", rt.auth(), rt.user(), rt.targetGets) + pages.POST("/target/list", rt.auth(), rt.user(), rt.targetGetsByHostFilter) + pages.DELETE("/targets", rt.auth(), rt.user(), rt.perm("/targets/del"), rt.targetDel) + pages.GET("/targets/tags", rt.auth(), rt.user(), rt.targetGetTags) + pages.POST("/targets/tags", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetBindTagsByFE) + pages.DELETE("/targets/tags", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetUnbindTagsByFE) + pages.PUT("/targets/note", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetUpdateNote) + pages.PUT("/targets/bgid", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetUpdateBgid) + + pages.POST("/builtin-cate-favorite", rt.auth(), rt.user(), rt.builtinCateFavoriteAdd) + pages.DELETE("/builtin-cate-favorite/:name", rt.auth(), rt.user(), rt.builtinCateFavoriteDel) + + pages.GET("/builtin-boards", rt.builtinBoardGets) + pages.GET("/builtin-board/:name", rt.builtinBoardGet) + pages.GET("/dashboards/builtin/list", rt.builtinBoardGets) + pages.GET("/builtin-boards-cates", rt.auth(), rt.user(), rt.builtinBoardCateGets) + pages.POST("/builtin-boards-detail", rt.auth(), rt.user(), rt.builtinBoardDetailGets) + pages.GET("/integrations/icon/:cate/:name", func(c *gin.Context) { + cate := ginx.UrlParamStr(c, "cate") + fp := "integrations/" + cate + "/icon/" + ginx.UrlParamStr(c, "name") + c.File(path.Join(fp)) + }) + + pages.GET("/busi-group/:id/boards", rt.auth(), rt.user(), rt.perm("/dashboards"), rt.bgro(), rt.boardGets) + pages.POST("/busi-group/:id/boards", rt.auth(), rt.user(), rt.perm("/dashboards/add"), rt.bgrw(), rt.boardAdd) + pages.POST("/busi-group/:id/board/:bid/clone", rt.auth(), rt.user(), rt.perm("/dashboards/add"), rt.bgrw(), rt.boardClone) + + pages.GET("/board/:bid", rt.boardGet) + pages.GET("/board/:bid/pure", rt.boardPureGet) + pages.PUT("/board/:bid", rt.auth(), rt.user(), rt.perm("/dashboards/put"), rt.boardPut) + pages.PUT("/board/:bid/configs", rt.auth(), rt.user(), rt.perm("/dashboards/put"), rt.boardPutConfigs) + pages.PUT("/board/:bid/public", rt.auth(), rt.user(), rt.perm("/dashboards/put"), rt.boardPutPublic) + pages.DELETE("/boards", rt.auth(), rt.user(), rt.perm("/dashboards/del"), rt.boardDel) + + pages.GET("/share-charts", rt.chartShareGets) + pages.POST("/share-charts", rt.auth(), rt.chartShareAdd) + + pages.GET("/alert-rules/builtin/list", rt.auth(), rt.user(), rt.alertRuleBuiltinList) + pages.GET("/alert-rules/builtin/alerts-cates", rt.auth(), rt.user(), rt.builtinAlertCateGets) + + pages.POST("/busi-group/:id/alert-rules/builtin", rt.auth(), rt.user(), rt.perm("/alert-rules/add"), rt.bgrw(), rt.alertRuleBuiltinImport) + pages.GET("/busi-group/:id/alert-rules", rt.auth(), rt.user(), rt.perm("/alert-rules"), rt.alertRuleGets) + pages.POST("/busi-group/:id/alert-rules", rt.auth(), rt.user(), rt.perm("/alert-rules/add"), rt.bgrw(), rt.alertRuleAddByFE) + pages.DELETE("/busi-group/:id/alert-rules", rt.auth(), rt.user(), rt.perm("/alert-rules/del"), rt.bgrw(), rt.alertRuleDel) + pages.PUT("/busi-group/:id/alert-rules/fields", rt.auth(), rt.user(), rt.perm("/alert-rules/put"), rt.bgrw(), rt.alertRulePutFields) + pages.PUT("/busi-group/:id/alert-rule/:arid", rt.auth(), rt.user(), rt.perm("/alert-rules/put"), rt.alertRulePutByFE) + pages.GET("/alert-rule/:arid", rt.auth(), rt.user(), rt.perm("/alert-rules"), rt.alertRuleGet) + + pages.GET("/busi-group/:id/recording-rules", rt.auth(), rt.user(), rt.perm("/recording-rules"), rt.recordingRuleGets) + pages.POST("/busi-group/:id/recording-rules", rt.auth(), rt.user(), rt.perm("/recording-rules/add"), rt.bgrw(), rt.recordingRuleAddByFE) + pages.DELETE("/busi-group/:id/recording-rules", rt.auth(), rt.user(), rt.perm("/recording-rules/del"), rt.bgrw(), rt.recordingRuleDel) + pages.PUT("/busi-group/:id/recording-rule/:rrid", rt.auth(), rt.user(), rt.perm("/recording-rules/put"), rt.bgrw(), rt.recordingRulePutByFE) + pages.GET("/recording-rule/:rrid", rt.auth(), rt.user(), rt.perm("/recording-rules"), rt.recordingRuleGet) + pages.PUT("/busi-group/:id/recording-rules/fields", rt.auth(), rt.user(), rt.perm("/recording-rules/put"), rt.recordingRulePutFields) + + pages.GET("/busi-group/:id/alert-mutes", rt.auth(), rt.user(), rt.perm("/alert-mutes"), rt.bgro(), rt.alertMuteGetsByBG) + pages.POST("/busi-group/:id/alert-mutes", rt.auth(), rt.user(), rt.perm("/alert-mutes/add"), rt.bgrw(), rt.alertMuteAdd) + pages.DELETE("/busi-group/:id/alert-mutes", rt.auth(), rt.user(), rt.perm("/alert-mutes/del"), rt.bgrw(), rt.alertMuteDel) + pages.PUT("/busi-group/:id/alert-mute/:amid", rt.auth(), rt.user(), rt.perm("/alert-mutes/put"), rt.alertMutePutByFE) + pages.PUT("/busi-group/:id/alert-mutes/fields", rt.auth(), rt.user(), rt.perm("/alert-mutes/put"), rt.bgrw(), rt.alertMutePutFields) + + pages.GET("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes"), rt.bgro(), rt.alertSubscribeGets) + pages.GET("/alert-subscribe/:sid", rt.auth(), rt.user(), rt.perm("/alert-subscribes"), rt.alertSubscribeGet) + pages.POST("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes/add"), rt.bgrw(), rt.alertSubscribeAdd) + pages.PUT("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes/put"), rt.bgrw(), rt.alertSubscribePut) + pages.DELETE("/busi-group/:id/alert-subscribes", rt.auth(), rt.user(), rt.perm("/alert-subscribes/del"), rt.bgrw(), rt.alertSubscribeDel) + + if rt.Center.AnonymousAccess.AlertDetail { + pages.GET("/alert-cur-event/:eid", rt.alertCurEventGet) + pages.GET("/alert-his-event/:eid", rt.alertHisEventGet) + } else { + pages.GET("/alert-cur-event/:eid", rt.auth(), rt.alertCurEventGet) + pages.GET("/alert-his-event/:eid", rt.auth(), rt.alertHisEventGet) + } + + // card logic + pages.GET("/alert-cur-events/list", rt.auth(), rt.alertCurEventsList) + pages.GET("/alert-cur-events/card", rt.auth(), rt.alertCurEventsCard) + pages.POST("/alert-cur-events/card/details", rt.auth(), rt.alertCurEventsCardDetails) + pages.GET("/alert-his-events/list", rt.auth(), rt.alertHisEventsList) + pages.DELETE("/alert-cur-events", rt.auth(), rt.user(), rt.perm("/alert-cur-events/del"), rt.alertCurEventDel) + + pages.GET("/alert-aggr-views", rt.auth(), rt.alertAggrViewGets) + pages.DELETE("/alert-aggr-views", rt.auth(), rt.user(), rt.alertAggrViewDel) + pages.POST("/alert-aggr-views", rt.auth(), rt.user(), rt.alertAggrViewAdd) + pages.PUT("/alert-aggr-views", rt.auth(), rt.user(), rt.alertAggrViewPut) + + pages.GET("/busi-group/:id/task-tpls", rt.auth(), rt.user(), rt.perm("/job-tpls"), rt.bgro(), rt.taskTplGets) + pages.POST("/busi-group/:id/task-tpls", rt.auth(), rt.user(), rt.perm("/job-tpls/add"), rt.bgrw(), rt.taskTplAdd) + pages.DELETE("/busi-group/:id/task-tpl/:tid", rt.auth(), rt.user(), rt.perm("/job-tpls/del"), rt.bgrw(), rt.taskTplDel) + pages.POST("/busi-group/:id/task-tpls/tags", rt.auth(), rt.user(), rt.perm("/job-tpls/put"), rt.bgrw(), rt.taskTplBindTags) + pages.DELETE("/busi-group/:id/task-tpls/tags", rt.auth(), rt.user(), rt.perm("/job-tpls/put"), rt.bgrw(), rt.taskTplUnbindTags) + pages.GET("/busi-group/:id/task-tpl/:tid", rt.auth(), rt.user(), rt.perm("/job-tpls"), rt.bgro(), rt.taskTplGet) + pages.PUT("/busi-group/:id/task-tpl/:tid", rt.auth(), rt.user(), rt.perm("/job-tpls/put"), rt.bgrw(), rt.taskTplPut) + + pages.GET("/busi-group/:id/tasks", rt.auth(), rt.user(), rt.perm("/job-tasks"), rt.bgro(), rt.taskGets) + pages.POST("/busi-group/:id/tasks", rt.auth(), rt.user(), rt.perm("/job-tasks/add"), rt.bgrw(), rt.taskAdd) + pages.GET("/busi-group/:id/task/*url", rt.auth(), rt.user(), rt.perm("/job-tasks"), rt.taskProxy) + pages.PUT("/busi-group/:id/task/*url", rt.auth(), rt.user(), rt.perm("/job-tasks/put"), rt.bgrw(), rt.taskProxy) + + pages.GET("/servers", rt.auth(), rt.admin(), rt.serversGet) + pages.GET("/server-clusters", rt.auth(), rt.admin(), rt.serverClustersGet) + + pages.POST("/datasource/list", rt.auth(), rt.datasourceList) + pages.POST("/datasource/plugin/list", rt.auth(), rt.pluginList) + pages.POST("/datasource/upsert", rt.auth(), rt.admin(), rt.datasourceUpsert) + pages.POST("/datasource/desc", rt.auth(), rt.admin(), rt.datasourceGet) + pages.POST("/datasource/status/update", rt.auth(), rt.admin(), rt.datasourceUpdataStatus) + pages.DELETE("/datasource/", rt.auth(), rt.admin(), rt.datasourceDel) + + pages.GET("/roles", rt.auth(), rt.admin(), rt.roleGets) + pages.POST("/roles", rt.auth(), rt.admin(), rt.roleAdd) + pages.PUT("/roles", rt.auth(), rt.admin(), rt.rolePut) + pages.DELETE("/role/:id", rt.auth(), rt.admin(), rt.roleDel) + + pages.GET("/role/:id/ops", rt.auth(), rt.admin(), rt.operationOfRole) + pages.PUT("/role/:id/ops", rt.auth(), rt.admin(), rt.roleBindOperation) + pages.GET("operation", rt.operations) + + pages.GET("/notify-tpls", rt.auth(), rt.admin(), rt.notifyTplGets) + pages.PUT("/notify-tpl/content", rt.auth(), rt.admin(), rt.notifyTplUpdateContent) + pages.PUT("/notify-tpl", rt.auth(), rt.admin(), rt.notifyTplUpdate) + pages.POST("/notify-tpl/preview", rt.auth(), rt.admin(), rt.notifyTplPreview) + + pages.GET("/sso-configs", rt.auth(), rt.admin(), rt.ssoConfigGets) + pages.PUT("/sso-config", rt.auth(), rt.admin(), rt.ssoConfigUpdate) + + pages.GET("/webhooks", rt.auth(), rt.admin(), rt.webhookGets) + pages.PUT("/webhooks", rt.auth(), rt.admin(), rt.webhookPuts) + + pages.GET("/notify-script", rt.auth(), rt.admin(), rt.notifyScriptGet) + pages.PUT("/notify-script", rt.auth(), rt.admin(), rt.notifyScriptPut) + + pages.GET("/notify-channel", rt.auth(), rt.admin(), rt.notifyChannelGets) + pages.PUT("/notify-channel", rt.auth(), rt.admin(), rt.notifyChannelPuts) + + pages.GET("/notify-contact", rt.auth(), rt.admin(), rt.notifyContactGets) + pages.PUT("/notify-contact", rt.auth(), rt.admin(), rt.notifyContactPuts) + } + + service := r.Group("/v1/n9e") + + if len(rt.Center.BasicAuth) > 0 { + service.Use(gin.BasicAuth(rt.Center.BasicAuth)) + } + { + service.Any("/prometheus/*url", rt.dsProxy) + service.POST("/users", rt.userAddPost) + service.GET("/users", rt.userFindAll) + + service.GET("/targets", rt.targetGets) + service.GET("/targets/tags", rt.targetGetTags) + service.POST("/targets/tags", rt.targetBindTagsByService) + service.DELETE("/targets/tags", rt.targetUnbindTagsByService) + service.PUT("/targets/note", rt.targetUpdateNoteByService) + + service.POST("/alert-rules", rt.alertRuleAddByService) + service.DELETE("/alert-rules", rt.alertRuleDelByService) + service.PUT("/alert-rule/:arid", rt.alertRulePutByService) + service.GET("/alert-rule/:arid", rt.alertRuleGet) + service.GET("/alert-rules", rt.alertRulesGetByService) + + service.GET("/alert-mutes", rt.alertMuteGets) + service.POST("/alert-mutes", rt.alertMuteAddByService) + service.DELETE("/alert-mutes", rt.alertMuteDel) + + service.GET("/alert-cur-events", rt.alertCurEventsList) + service.GET("/alert-his-events", rt.alertHisEventsList) + service.GET("/alert-his-event/:eid", rt.alertHisEventGet) + + service.GET("/config/:id", rt.configGet) + service.GET("/configs", rt.configsGet) + service.PUT("/configs", rt.configsPut) + service.POST("/configs", rt.configsPost) + service.DELETE("/configs", rt.configsDel) + + service.POST("/conf-prop/encrypt", rt.confPropEncrypt) + service.POST("/conf-prop/decrypt", rt.confPropDecrypt) + } + + rt.configNoRoute(r) +} + +func Render(c *gin.Context, data, msg interface{}) { + if msg == nil { + if data == nil { + data = struct{}{} + } + c.JSON(http.StatusOK, gin.H{"data": data, "error": ""}) + } else { + c.JSON(http.StatusOK, gin.H{"error": gin.H{"message": msg}}) + } +} + +func Dangerous(c *gin.Context, v interface{}, code ...int) { + if v == nil { + return + } + + switch t := v.(type) { + case string: + if t != "" { + c.JSON(http.StatusOK, gin.H{"error": gin.H{"message": v}}) + } + case error: + c.JSON(http.StatusOK, gin.H{"error": gin.H{"message": t.Error()}}) + } +} diff --git a/src/webapi/router/router_alert_aggr_view.go b/center/router/router_alert_aggr_view.go similarity index 61% rename from src/webapi/router/router_alert_aggr_view.go rename to center/router/router_alert_aggr_view.go index 57780144786ae6415dd6c224ac842071de3add8f..20a0bbc33d9a7543595ceb627faa2a023bbf0f37 100644 --- a/src/webapi/router/router_alert_aggr_view.go +++ b/center/router/router_alert_aggr_view.go @@ -3,19 +3,20 @@ package router import ( "net/http" - "github.com/didi/nightingale/v5/src/models" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" ) // no param -func alertAggrViewGets(c *gin.Context) { - lst, err := models.AlertAggrViewGets(c.MustGet("userid")) +func (rt *Router) alertAggrViewGets(c *gin.Context) { + lst, err := models.AlertAggrViewGets(rt.Ctx, c.MustGet("userid")) ginx.NewRender(c).Data(lst, err) } // body: name, rule, cate -func alertAggrViewAdd(c *gin.Context) { +func (rt *Router) alertAggrViewAdd(c *gin.Context) { var f models.AlertAggrView ginx.BindJSON(c, &f) @@ -27,31 +28,31 @@ func alertAggrViewAdd(c *gin.Context) { f.Id = 0 f.CreateBy = me.Id - ginx.Dangerous(f.Add()) + ginx.Dangerous(f.Add(rt.Ctx)) ginx.NewRender(c).Data(f, nil) } // body: ids -func alertAggrViewDel(c *gin.Context) { +func (rt *Router) alertAggrViewDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() me := c.MustGet("user").(*models.User) if me.IsAdmin() { - ginx.NewRender(c).Message(models.AlertAggrViewDel(f.Ids)) + ginx.NewRender(c).Message(models.AlertAggrViewDel(rt.Ctx, f.Ids)) } else { - ginx.NewRender(c).Message(models.AlertAggrViewDel(f.Ids, me.Id)) + ginx.NewRender(c).Message(models.AlertAggrViewDel(rt.Ctx, f.Ids, me.Id)) } } // body: id, name, rule, cate -func alertAggrViewPut(c *gin.Context) { +func (rt *Router) alertAggrViewPut(c *gin.Context) { var f models.AlertAggrView ginx.BindJSON(c, &f) - view, err := models.AlertAggrViewGet("id = ?", f.Id) + view, err := models.AlertAggrViewGet(rt.Ctx, "id = ?", f.Id) ginx.Dangerous(err) if view == nil { @@ -69,5 +70,5 @@ func alertAggrViewPut(c *gin.Context) { } } - ginx.NewRender(c).Message(view.Update(f.Name, f.Rule, f.Cate, me.Id)) + ginx.NewRender(c).Message(view.Update(rt.Ctx, f.Name, f.Rule, f.Cate, me.Id)) } diff --git a/src/webapi/router/router_alert_cur_event.go b/center/router/router_alert_cur_event.go similarity index 70% rename from src/webapi/router/router_alert_cur_event.go rename to center/router/router_alert_cur_event.go index 1b304dc6eb28ea59e86a62c10703d30049adcb1f..d7a207b917a08dc3e62ff014219702b152e861e1 100644 --- a/src/webapi/router/router_alert_cur_event.go +++ b/center/router/router_alert_cur_event.go @@ -5,10 +5,10 @@ import ( "sort" "strings" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" ) func parseAggrRules(c *gin.Context) []*models.AggrRule { @@ -38,14 +38,20 @@ func parseAggrRules(c *gin.Context) []*models.AggrRule { return rules } -func alertCurEventsCard(c *gin.Context) { +func (rt *Router) alertCurEventsCard(c *gin.Context) { stime, etime := getTimeRange(c) severity := ginx.QueryInt(c, "severity", -1) query := ginx.QueryStr(c, "query", "") busiGroupId := ginx.QueryInt64(c, "bgid", 0) - clusters := queryClusters(c) + dsIds := queryDatasourceIds(c) rules := parseAggrRules(c) - prod := ginx.QueryStr(c, "prod", "") + + prod := ginx.QueryStr(c, "prods", "") + prods := []string{} + if prod != "" { + prods = strings.Split(prod, ",") + } + cate := ginx.QueryStr(c, "cate", "$all") cates := []string{} if cate != "$all" { @@ -53,7 +59,7 @@ func alertCurEventsCard(c *gin.Context) { } // 最多获取50000个,获取太多也没啥意义 - list, err := models.AlertCurEventGets(prod, busiGroupId, stime, etime, severity, clusters, cates, query, 50000, 0) + list, err := models.AlertCurEventGets(rt.Ctx, prods, busiGroupId, stime, etime, severity, dsIds, cates, query, 50000, 0) ginx.Dangerous(err) cardmap := make(map[string]*AlertCard) @@ -104,15 +110,15 @@ type AlertCard struct { Severity int `json:"severity"` } -func alertCurEventsCardDetails(c *gin.Context) { +func (rt *Router) alertCurEventsCardDetails(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) - list, err := models.AlertCurEventGetByIds(f.Ids) + list, err := models.AlertCurEventGetByIds(rt.Ctx, f.Ids) if err == nil { cache := make(map[int64]*models.UserGroup) for i := 0; i < len(list); i++ { - list[i].FillNotifyGroups(cache) + list[i].FillNotifyGroups(rt.Ctx, cache) } } @@ -120,29 +126,39 @@ func alertCurEventsCardDetails(c *gin.Context) { } // 列表方式,拉取活跃告警 -func alertCurEventsList(c *gin.Context) { +func (rt *Router) alertCurEventsList(c *gin.Context) { stime, etime := getTimeRange(c) severity := ginx.QueryInt(c, "severity", -1) query := ginx.QueryStr(c, "query", "") limit := ginx.QueryInt(c, "limit", 20) busiGroupId := ginx.QueryInt64(c, "bgid", 0) - clusters := queryClusters(c) - prod := ginx.QueryStr(c, "prod", "") + dsIds := queryDatasourceIds(c) + + prod := ginx.QueryStr(c, "prods", "") + if prod == "" { + prod = ginx.QueryStr(c, "rule_prods", "") + } + + prods := []string{} + if prod != "" { + prods = strings.Split(prod, ",") + } + cate := ginx.QueryStr(c, "cate", "$all") cates := []string{} if cate != "$all" { cates = strings.Split(cate, ",") } - total, err := models.AlertCurEventTotal(prod, busiGroupId, stime, etime, severity, clusters, cates, query) + total, err := models.AlertCurEventTotal(rt.Ctx, prods, busiGroupId, stime, etime, severity, dsIds, cates, query) ginx.Dangerous(err) - list, err := models.AlertCurEventGets(prod, busiGroupId, stime, etime, severity, clusters, cates, query, limit, ginx.Offset(c, limit)) + list, err := models.AlertCurEventGets(rt.Ctx, prods, busiGroupId, stime, etime, severity, dsIds, cates, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) cache := make(map[int64]*models.UserGroup) for i := 0; i < len(list); i++ { - list[i].FillNotifyGroups(cache) + list[i].FillNotifyGroups(rt.Ctx, cache) } ginx.NewRender(c).Data(gin.H{ @@ -151,7 +167,7 @@ func alertCurEventsList(c *gin.Context) { }, nil) } -func alertCurEventDel(c *gin.Context) { +func (rt *Router) alertCurEventDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() @@ -159,21 +175,21 @@ func alertCurEventDel(c *gin.Context) { set := make(map[int64]struct{}) for i := 0; i < len(f.Ids); i++ { - event, err := models.AlertCurEventGetById(f.Ids[i]) + event, err := models.AlertCurEventGetById(rt.Ctx, f.Ids[i]) ginx.Dangerous(err) if _, has := set[event.GroupId]; !has { - bgrwCheck(c, event.GroupId) + rt.bgrwCheck(c, event.GroupId) set[event.GroupId] = struct{}{} } } - ginx.NewRender(c).Message(models.AlertCurEventDel(f.Ids)) + ginx.NewRender(c).Message(models.AlertCurEventDel(rt.Ctx, f.Ids)) } -func alertCurEventGet(c *gin.Context) { +func (rt *Router) alertCurEventGet(c *gin.Context) { eid := ginx.UrlParamInt64(c, "eid") - event, err := models.AlertCurEventGetById(eid) + event, err := models.AlertCurEventGetById(rt.Ctx, eid) ginx.Dangerous(err) if event == nil { diff --git a/src/webapi/router/router_alert_his_event.go b/center/router/router_alert_his_event.go similarity index 61% rename from src/webapi/router/router_alert_his_event.go rename to center/router/router_alert_his_event.go index 555d47463bfa3e5343d4c0d4cb219ce90ce526af..b1a50d085892f79d83a35f50b06598902103ca4c 100644 --- a/src/webapi/router/router_alert_his_event.go +++ b/center/router/router_alert_his_event.go @@ -4,10 +4,10 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" ) func getTimeRange(c *gin.Context) (stime, etime int64) { @@ -26,7 +26,7 @@ func getTimeRange(c *gin.Context) (stime, etime int64) { return } -func alertHisEventsList(c *gin.Context) { +func (rt *Router) alertHisEventsList(c *gin.Context) { stime, etime := getTimeRange(c) severity := ginx.QueryInt(c, "severity", -1) @@ -34,23 +34,33 @@ func alertHisEventsList(c *gin.Context) { query := ginx.QueryStr(c, "query", "") limit := ginx.QueryInt(c, "limit", 20) busiGroupId := ginx.QueryInt64(c, "bgid", 0) - clusters := queryClusters(c) - prod := ginx.QueryStr(c, "prod", "") + dsIds := queryDatasourceIds(c) + + prod := ginx.QueryStr(c, "prods", "") + if prod == "" { + prod = ginx.QueryStr(c, "rule_prods", "") + } + + prods := []string{} + if prod != "" { + prods = strings.Split(prod, ",") + } + cate := ginx.QueryStr(c, "cate", "$all") cates := []string{} if cate != "$all" { cates = strings.Split(cate, ",") } - total, err := models.AlertHisEventTotal(prod, busiGroupId, stime, etime, severity, recovered, clusters, cates, query) + total, err := models.AlertHisEventTotal(rt.Ctx, prods, busiGroupId, stime, etime, severity, recovered, dsIds, cates, query) ginx.Dangerous(err) - list, err := models.AlertHisEventGets(prod, busiGroupId, stime, etime, severity, recovered, clusters, cates, query, limit, ginx.Offset(c, limit)) + list, err := models.AlertHisEventGets(rt.Ctx, prods, busiGroupId, stime, etime, severity, recovered, dsIds, cates, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) cache := make(map[int64]*models.UserGroup) for i := 0; i < len(list); i++ { - list[i].FillNotifyGroups(cache) + list[i].FillNotifyGroups(rt.Ctx, cache) } ginx.NewRender(c).Data(gin.H{ @@ -59,9 +69,9 @@ func alertHisEventsList(c *gin.Context) { }, nil) } -func alertHisEventGet(c *gin.Context) { +func (rt *Router) alertHisEventGet(c *gin.Context) { eid := ginx.UrlParamInt64(c, "eid") - event, err := models.AlertHisEventGetById(eid) + event, err := models.AlertHisEventGetById(rt.Ctx, eid) ginx.Dangerous(err) if event == nil { diff --git a/src/webapi/router/router_alert_rule.go b/center/router/router_alert_rule.go similarity index 63% rename from src/webapi/router/router_alert_rule.go rename to center/router/router_alert_rule.go index 9ada06c11bc271219fcce8eaa52bba5fe9c782e2..406bab0a323459323001a882fe24bd1c089325f6 100644 --- a/src/webapi/router/router_alert_rule.go +++ b/center/router/router_alert_rule.go @@ -1,31 +1,32 @@ package router import ( + "encoding/json" "net/http" "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/i18n" - - "github.com/didi/nightingale/v5/src/models" ) // Return all, front-end search and paging -func alertRuleGets(c *gin.Context) { +func (rt *Router) alertRuleGets(c *gin.Context) { busiGroupId := ginx.UrlParamInt64(c, "id") - ars, err := models.AlertRuleGets(busiGroupId) + ars, err := models.AlertRuleGets(rt.Ctx, busiGroupId) if err == nil { cache := make(map[int64]*models.UserGroup) for i := 0; i < len(ars); i++ { - ars[i].FillNotifyGroups(cache) + ars[i].FillNotifyGroups(rt.Ctx, cache) } } ginx.NewRender(c).Data(ars, err) } -func alertRulesGetByService(c *gin.Context) { +func (rt *Router) alertRulesGetByService(c *gin.Context) { prods := strings.Split(ginx.QueryStr(c, "prods", ""), ",") query := ginx.QueryStr(c, "query", "") algorithm := ginx.QueryStr(c, "algorithm", "") @@ -37,18 +38,18 @@ func alertRulesGetByService(c *gin.Context) { } disabled := ginx.QueryInt(c, "disabled", -1) - ars, err := models.AlertRulesGetsBy(prods, query, algorithm, cluster, cates, disabled) + ars, err := models.AlertRulesGetsBy(rt.Ctx, prods, query, algorithm, cluster, cates, disabled) if err == nil { cache := make(map[int64]*models.UserGroup) for i := 0; i < len(ars); i++ { - ars[i].FillNotifyGroups(cache) + ars[i].FillNotifyGroups(rt.Ctx, cache) } } ginx.NewRender(c).Data(ars, err) } // single or import -func alertRuleAddByFE(c *gin.Context) { +func (rt *Router) alertRuleAddByFE(c *gin.Context) { username := c.MustGet("username").(string) var lst []models.AlertRule @@ -60,12 +61,12 @@ func alertRuleAddByFE(c *gin.Context) { } bgid := ginx.UrlParamInt64(c, "id") - reterr := alertRuleAdd(lst, username, bgid, c.GetHeader("X-Language")) + reterr := rt.alertRuleAdd(lst, username, bgid, c.GetHeader("X-Language")) ginx.NewRender(c).Data(reterr, nil) } -func alertRuleAddByService(c *gin.Context) { +func (rt *Router) alertRuleAddByService(c *gin.Context) { var lst []models.AlertRule ginx.BindJSON(c, &lst) @@ -73,11 +74,11 @@ func alertRuleAddByService(c *gin.Context) { if count == 0 { ginx.Bomb(http.StatusBadRequest, "input json is empty") } - reterr := alertRuleAddForService(lst, "") + reterr := rt.alertRuleAddForService(lst, "") ginx.NewRender(c).Data(reterr, nil) } -func alertRuleAddForService(lst []models.AlertRule, username string) map[string]string { +func (rt *Router) alertRuleAddForService(lst []models.AlertRule, username string) map[string]string { count := len(lst) // alert rule name -> error string reterr := make(map[string]string) @@ -93,7 +94,7 @@ func alertRuleAddForService(lst []models.AlertRule, username string) map[string] continue } - if err := lst[i].Add(); err != nil { + if err := lst[i].Add(rt.Ctx, models.GetChannelMap(rt.Ctx)); err != nil { reterr[lst[i].Name] = err.Error() } else { reterr[lst[i].Name] = "" @@ -102,7 +103,7 @@ func alertRuleAddForService(lst []models.AlertRule, username string) map[string] return reterr } -func alertRuleAdd(lst []models.AlertRule, username string, bgid int64, lang string) map[string]string { +func (rt *Router) alertRuleAdd(lst []models.AlertRule, username string, bgid int64, lang string) map[string]string { count := len(lst) // alert rule name -> error string reterr := make(map[string]string) @@ -119,7 +120,7 @@ func alertRuleAdd(lst []models.AlertRule, username string, bgid int64, lang stri continue } - if err := lst[i].Add(); err != nil { + if err := lst[i].Add(rt.Ctx, models.GetChannelMap(rt.Ctx)); err != nil { reterr[lst[i].Name] = i18n.Sprintf(lang, err.Error()) } else { reterr[lst[i].Name] = "" @@ -128,28 +129,28 @@ func alertRuleAdd(lst []models.AlertRule, username string, bgid int64, lang stri return reterr } -func alertRuleDel(c *gin.Context) { +func (rt *Router) alertRuleDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() // param(busiGroupId) for protect - ginx.NewRender(c).Message(models.AlertRuleDels(f.Ids, ginx.UrlParamInt64(c, "id"))) + ginx.NewRender(c).Message(models.AlertRuleDels(rt.Ctx, f.Ids, ginx.UrlParamInt64(c, "id"))) } -func alertRuleDelByService(c *gin.Context) { +func (rt *Router) alertRuleDelByService(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() - ginx.NewRender(c).Message(models.AlertRuleDels(f.Ids)) + ginx.NewRender(c).Message(models.AlertRuleDels(rt.Ctx, f.Ids)) } -func alertRulePutByFE(c *gin.Context) { +func (rt *Router) alertRulePutByFE(c *gin.Context) { var f models.AlertRule ginx.BindJSON(c, &f) arid := ginx.UrlParamInt64(c, "arid") - ar, err := models.AlertRuleGetById(arid) + ar, err := models.AlertRuleGetById(rt.Ctx, arid) ginx.Dangerous(err) if ar == nil { @@ -157,25 +158,25 @@ func alertRulePutByFE(c *gin.Context) { return } - bgrwCheck(c, ar.GroupId) + rt.bgrwCheck(c, ar.GroupId) f.UpdateBy = c.MustGet("username").(string) - ginx.NewRender(c).Message(ar.Update(f)) + ginx.NewRender(c).Message(ar.Update(rt.Ctx, f, models.GetChannelMap(rt.Ctx))) } -func alertRulePutByService(c *gin.Context) { +func (rt *Router) alertRulePutByService(c *gin.Context) { var f models.AlertRule ginx.BindJSON(c, &f) arid := ginx.UrlParamInt64(c, "arid") - ar, err := models.AlertRuleGetById(arid) + ar, err := models.AlertRuleGetById(rt.Ctx, arid) ginx.Dangerous(err) if ar == nil { ginx.NewRender(c, http.StatusNotFound).Message("No such AlertRule") return } - ginx.NewRender(c).Message(ar.Update(f)) + ginx.NewRender(c).Message(ar.Update(rt.Ctx, f, models.GetChannelMap(rt.Ctx))) } type alertRuleFieldForm struct { @@ -185,7 +186,7 @@ type alertRuleFieldForm struct { } // update one field: cluster note severity disabled prom_eval_interval prom_for_duration notify_channels notify_groups notify_recovered notify_repeat_step callbacks runbook_url append_tags -func alertRulePutFields(c *gin.Context) { +func (rt *Router) alertRulePutFields(c *gin.Context) { var f alertRuleFieldForm ginx.BindJSON(c, &f) @@ -197,7 +198,7 @@ func alertRulePutFields(c *gin.Context) { f.Fields["update_at"] = time.Now().Unix() for i := 0; i < len(f.Ids); i++ { - ar, err := models.AlertRuleGetById(f.Ids[i]) + ar, err := models.AlertRuleGetById(rt.Ctx, f.Ids[i]) ginx.Dangerous(err) if ar == nil { @@ -209,7 +210,7 @@ func alertRulePutFields(c *gin.Context) { if callbacks, has := f.Fields["callbacks"]; has { callback := callbacks.(string) if !strings.Contains(ar.Callbacks, callback) { - ginx.Dangerous(ar.UpdateFieldsMap(map[string]interface{}{"callbacks": ar.Callbacks + " " + callback})) + ginx.Dangerous(ar.UpdateFieldsMap(rt.Ctx, map[string]interface{}{"callbacks": ar.Callbacks + " " + callback})) continue } } @@ -219,21 +220,29 @@ func alertRulePutFields(c *gin.Context) { // 删除一个 callback 地址 if callbacks, has := f.Fields["callbacks"]; has { callback := callbacks.(string) - ginx.Dangerous(ar.UpdateFieldsMap(map[string]interface{}{"callbacks": strings.ReplaceAll(ar.Callbacks, callback, "")})) + ginx.Dangerous(ar.UpdateFieldsMap(rt.Ctx, map[string]interface{}{"callbacks": strings.ReplaceAll(ar.Callbacks, callback, "")})) continue } } - ginx.Dangerous(ar.UpdateFieldsMap(f.Fields)) + for k, v := range f.Fields { + if k == "datasource_ids" { + b, err := json.Marshal(v) + ginx.Dangerous(err) + f.Fields[k] = string(b) + } + } + + ginx.Dangerous(ar.UpdateFieldsMap(rt.Ctx, f.Fields)) } ginx.NewRender(c).Message(nil) } -func alertRuleGet(c *gin.Context) { +func (rt *Router) alertRuleGet(c *gin.Context) { arid := ginx.UrlParamInt64(c, "arid") - ar, err := models.AlertRuleGetById(arid) + ar, err := models.AlertRuleGetById(rt.Ctx, arid) ginx.Dangerous(err) if ar == nil { @@ -241,6 +250,8 @@ func alertRuleGet(c *gin.Context) { return } - err = ar.FillNotifyGroups(make(map[int64]*models.UserGroup)) + err = ar.FillNotifyGroups(rt.Ctx, make(map[int64]*models.UserGroup)) + ginx.Dangerous(err) + ginx.NewRender(c).Data(ar, err) } diff --git a/src/webapi/router/router_alert_subscribe.go b/center/router/router_alert_subscribe.go similarity index 59% rename from src/webapi/router/router_alert_subscribe.go rename to center/router/router_alert_subscribe.go index 431cc45a65be793e4ccae7c0e5f76e61753d2eb0..b8d762321e332f6846d70ea2e2131e04ffd59ef1 100644 --- a/src/webapi/router/router_alert_subscribe.go +++ b/center/router/router_alert_subscribe.go @@ -4,34 +4,38 @@ import ( "net/http" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" ) // Return all, front-end search and paging -func alertSubscribeGets(c *gin.Context) { +func (rt *Router) alertSubscribeGets(c *gin.Context) { bgid := ginx.UrlParamInt64(c, "id") - lst, err := models.AlertSubscribeGets(bgid) + lst, err := models.AlertSubscribeGets(rt.Ctx, bgid) if err == nil { ugcache := make(map[int64]*models.UserGroup) for i := 0; i < len(lst); i++ { - ginx.Dangerous(lst[i].FillUserGroups(ugcache)) + ginx.Dangerous(lst[i].FillUserGroups(rt.Ctx, ugcache)) } rulecache := make(map[int64]string) for i := 0; i < len(lst); i++ { - ginx.Dangerous(lst[i].FillRuleName(rulecache)) + ginx.Dangerous(lst[i].FillRuleName(rt.Ctx, rulecache)) + } + + for i := 0; i < len(lst); i++ { + ginx.Dangerous(lst[i].FillDatasourceIds(rt.Ctx)) } } ginx.NewRender(c).Data(lst, err) } -func alertSubscribeGet(c *gin.Context) { +func (rt *Router) alertSubscribeGet(c *gin.Context) { subid := ginx.UrlParamInt64(c, "sid") - sub, err := models.AlertSubscribeGet("id=?", subid) + sub, err := models.AlertSubscribeGet(rt.Ctx, "id=?", subid) ginx.Dangerous(err) if sub == nil { @@ -40,15 +44,17 @@ func alertSubscribeGet(c *gin.Context) { } ugcache := make(map[int64]*models.UserGroup) - ginx.Dangerous(sub.FillUserGroups(ugcache)) + ginx.Dangerous(sub.FillUserGroups(rt.Ctx, ugcache)) rulecache := make(map[int64]string) - ginx.Dangerous(sub.FillRuleName(rulecache)) + ginx.Dangerous(sub.FillRuleName(rt.Ctx, rulecache)) + ginx.Dangerous(sub.FillDatasourceIds(rt.Ctx)) + ginx.Dangerous(sub.DB2FE()) ginx.NewRender(c).Data(sub, nil) } -func alertSubscribeAdd(c *gin.Context) { +func (rt *Router) alertSubscribeAdd(c *gin.Context) { var f models.AlertSubscribe ginx.BindJSON(c, &f) @@ -61,10 +67,10 @@ func alertSubscribeAdd(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "group_id invalid") } - ginx.NewRender(c).Message(f.Add()) + ginx.NewRender(c).Message(f.Add(rt.Ctx)) } -func alertSubscribePut(c *gin.Context) { +func (rt *Router) alertSubscribePut(c *gin.Context) { var fs []models.AlertSubscribe ginx.BindJSON(c, &fs) @@ -74,6 +80,7 @@ func alertSubscribePut(c *gin.Context) { fs[i].UpdateBy = username fs[i].UpdateAt = timestamp ginx.Dangerous(fs[i].Update( + rt.Ctx, "name", "disabled", "cluster", @@ -86,16 +93,20 @@ func alertSubscribePut(c *gin.Context) { "user_group_ids", "update_at", "update_by", + "webhooks", + "for_duration", + "redefine_webhooks", + "datasource_ids", )) } ginx.NewRender(c).Message(nil) } -func alertSubscribeDel(c *gin.Context) { +func (rt *Router) alertSubscribeDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() - ginx.NewRender(c).Message(models.AlertSubscribeDel(f.Ids)) + ginx.NewRender(c).Message(models.AlertSubscribeDel(rt.Ctx, f.Ids)) } diff --git a/src/webapi/router/router_board.go b/center/router/router_board.go similarity index 54% rename from src/webapi/router/router_board.go rename to center/router/router_board.go index a59309ace82ccf1570326b28a5125a3a4d16bcaa..0abe402689c4e0b1290d17fce2736d906b57035f 100644 --- a/src/webapi/router/router_board.go +++ b/center/router/router_board.go @@ -4,7 +4,8 @@ import ( "net/http" "time" - "github.com/didi/nightingale/v5/src/models" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/toolkits/pkg/ginx" @@ -18,7 +19,7 @@ type boardForm struct { Public int `json:"public"` } -func boardAdd(c *gin.Context) { +func (rt *Router) boardAdd(c *gin.Context) { var f boardForm ginx.BindJSON(c, &f) @@ -34,19 +35,19 @@ func boardAdd(c *gin.Context) { UpdateBy: me.Username, } - err := board.Add() + err := board.Add(rt.Ctx) ginx.Dangerous(err) if f.Configs != "" { - ginx.Dangerous(models.BoardPayloadSave(board.Id, f.Configs)) + ginx.Dangerous(models.BoardPayloadSave(rt.Ctx, board.Id, f.Configs)) } ginx.NewRender(c).Data(board, nil) } -func boardGet(c *gin.Context) { +func (rt *Router) boardGet(c *gin.Context) { bid := ginx.UrlParamStr(c, "bid") - board, err := models.BoardGet("id = ? or ident = ?", bid, bid) + board, err := models.BoardGet(rt.Ctx, "id = ? or ident = ?", bid, bid) ginx.Dangerous(err) if board == nil { @@ -54,17 +55,21 @@ func boardGet(c *gin.Context) { } if board.Public == 0 { - auth()(c) - user()(c) + rt.auth()(c) + rt.user()(c) - bgroCheck(c, board.GroupId) + me := c.MustGet("user").(*models.User) + if !me.IsAdmin() { + // check permission + rt.bgroCheck(c, board.GroupId) + } } ginx.NewRender(c).Data(board, nil) } -func boardPureGet(c *gin.Context) { - board, err := models.BoardGetByID(ginx.UrlParamInt64(c, "bid")) +func (rt *Router) boardPureGet(c *gin.Context) { + board, err := models.BoardGetByID(rt.Ctx, ginx.UrlParamInt64(c, "bid")) ginx.Dangerous(err) if board == nil { @@ -75,7 +80,7 @@ func boardPureGet(c *gin.Context) { } // bgrwCheck -func boardDel(c *gin.Context) { +func (rt *Router) boardDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() @@ -83,24 +88,27 @@ func boardDel(c *gin.Context) { for i := 0; i < len(f.Ids); i++ { bid := f.Ids[i] - board, err := models.BoardGet("id = ?", bid) + board, err := models.BoardGet(rt.Ctx, "id = ?", bid) ginx.Dangerous(err) if board == nil { continue } - // check permission - bgrwCheck(c, board.GroupId) + me := c.MustGet("user").(*models.User) + if !me.IsAdmin() { + // check permission + rt.bgrwCheck(c, board.GroupId) + } - ginx.Dangerous(board.Del()) + ginx.Dangerous(board.Del(rt.Ctx)) } ginx.NewRender(c).Message(nil) } -func Board(id int64) *models.Board { - obj, err := models.BoardGet("id=?", id) +func (rt *Router) Board(id int64) *models.Board { + obj, err := models.BoardGet(rt.Ctx, "id=?", id) ginx.Dangerous(err) if obj == nil { @@ -111,17 +119,19 @@ func Board(id int64) *models.Board { } // bgrwCheck -func boardPut(c *gin.Context) { +func (rt *Router) boardPut(c *gin.Context) { var f boardForm ginx.BindJSON(c, &f) me := c.MustGet("user").(*models.User) - bo := Board(ginx.UrlParamInt64(c, "bid")) + bo := rt.Board(ginx.UrlParamInt64(c, "bid")) - // check permission - bgrwCheck(c, bo.GroupId) + if !me.IsAdmin() { + // check permission + rt.bgrwCheck(c, bo.GroupId) + } - can, err := bo.CanRenameIdent(f.Ident) + can, err := bo.CanRenameIdent(rt.Ctx, f.Ident) ginx.Dangerous(err) if !can { @@ -134,19 +144,19 @@ func boardPut(c *gin.Context) { bo.UpdateBy = me.Username bo.UpdateAt = time.Now().Unix() - err = bo.Update("name", "ident", "tags", "update_by", "update_at") + err = bo.Update(rt.Ctx, "name", "ident", "tags", "update_by", "update_at") ginx.NewRender(c).Data(bo, err) } // bgrwCheck -func boardPutConfigs(c *gin.Context) { +func (rt *Router) boardPutConfigs(c *gin.Context) { var f boardForm ginx.BindJSON(c, &f) me := c.MustGet("user").(*models.User) bid := ginx.UrlParamStr(c, "bid") - bo, err := models.BoardGet("id = ? or ident = ?", bid, bid) + bo, err := models.BoardGet(rt.Ctx, "id = ? or ident = ?", bid, bid) ginx.Dangerous(err) if bo == nil { @@ -154,48 +164,52 @@ func boardPutConfigs(c *gin.Context) { } // check permission - bgrwCheck(c, bo.GroupId) + if !me.IsAdmin() { + rt.bgrwCheck(c, bo.GroupId) + } bo.UpdateBy = me.Username bo.UpdateAt = time.Now().Unix() - ginx.Dangerous(bo.Update("update_by", "update_at")) + ginx.Dangerous(bo.Update(rt.Ctx, "update_by", "update_at")) bo.Configs = f.Configs - ginx.Dangerous(models.BoardPayloadSave(bo.Id, f.Configs)) + ginx.Dangerous(models.BoardPayloadSave(rt.Ctx, bo.Id, f.Configs)) ginx.NewRender(c).Data(bo, nil) } // bgrwCheck -func boardPutPublic(c *gin.Context) { +func (rt *Router) boardPutPublic(c *gin.Context) { var f boardForm ginx.BindJSON(c, &f) me := c.MustGet("user").(*models.User) - bo := Board(ginx.UrlParamInt64(c, "bid")) + bo := rt.Board(ginx.UrlParamInt64(c, "bid")) // check permission - bgrwCheck(c, bo.GroupId) + if !me.IsAdmin() { + rt.bgrwCheck(c, bo.GroupId) + } bo.Public = f.Public bo.UpdateBy = me.Username bo.UpdateAt = time.Now().Unix() - err := bo.Update("public", "update_by", "update_at") + err := bo.Update(rt.Ctx, "public", "update_by", "update_at") ginx.NewRender(c).Data(bo, err) } -func boardGets(c *gin.Context) { +func (rt *Router) boardGets(c *gin.Context) { bgid := ginx.UrlParamInt64(c, "id") query := ginx.QueryStr(c, "query", "") - boards, err := models.BoardGets(bgid, query) + boards, err := models.BoardGetsByGroupId(rt.Ctx, bgid, query) ginx.NewRender(c).Data(boards, err) } -func boardClone(c *gin.Context) { +func (rt *Router) boardClone(c *gin.Context) { me := c.MustGet("user").(*models.User) - bo := Board(ginx.UrlParamInt64(c, "bid")) + bo := rt.Board(ginx.UrlParamInt64(c, "bid")) newBoard := &models.Board{ Name: bo.Name + " Copy", @@ -209,53 +223,15 @@ func boardClone(c *gin.Context) { newBoard.Ident = uuid.NewString() } - ginx.Dangerous(newBoard.Add()) + ginx.Dangerous(newBoard.Add(rt.Ctx)) // clone payload - payload, err := models.BoardPayloadGet(bo.Id) + payload, err := models.BoardPayloadGet(rt.Ctx, bo.Id) ginx.Dangerous(err) if payload != "" { - ginx.Dangerous(models.BoardPayloadSave(newBoard.Id, payload)) + ginx.Dangerous(models.BoardPayloadSave(rt.Ctx, newBoard.Id, payload)) } ginx.NewRender(c).Message(nil) } - -// ---- migrate ---- - -func migrateDashboards(c *gin.Context) { - lst, err := models.DashboardGetAll() - ginx.NewRender(c).Data(lst, err) -} - -func migrateDashboardGet(c *gin.Context) { - dash := Dashboard(ginx.UrlParamInt64(c, "id")) - ginx.NewRender(c).Data(dash, nil) -} - -func migrateDashboard(c *gin.Context) { - dash := Dashboard(ginx.UrlParamInt64(c, "id")) - - var f boardForm - ginx.BindJSON(c, &f) - - me := c.MustGet("user").(*models.User) - - board := &models.Board{ - GroupId: dash.GroupId, - Name: f.Name, - Tags: f.Tags, - Configs: f.Configs, - CreateBy: me.Username, - UpdateBy: me.Username, - } - - ginx.Dangerous(board.Add()) - - if board.Configs != "" { - ginx.Dangerous(models.BoardPayloadSave(board.Id, board.Configs)) - } - - ginx.NewRender(c).Message(dash.Del()) -} diff --git a/center/router/router_builtin.go b/center/router/router_builtin.go new file mode 100644 index 0000000000000000000000000000000000000000..83d0d149510acdcb7a9a9ae15be6d28513c113cc --- /dev/null +++ b/center/router/router_builtin.go @@ -0,0 +1,317 @@ +package router + +import ( + "encoding/json" + "fmt" + "net/http" + "path" + "strings" + + "github.com/ccfos/nightingale/v6/models" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/ginx" + "github.com/toolkits/pkg/i18n" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/runner" +) + +// 创建 builtin_cate +func (rt *Router) builtinCateFavoriteAdd(c *gin.Context) { + var f models.BuiltinCate + ginx.BindJSON(c, &f) + + if f.Name == "" { + ginx.Bomb(http.StatusBadRequest, "name is empty") + } + + me := c.MustGet("user").(*models.User) + f.UserId = me.Id + + ginx.NewRender(c).Message(f.Create(rt.Ctx)) +} + +// 删除 builtin_cate +func (rt *Router) builtinCateFavoriteDel(c *gin.Context) { + name := ginx.UrlParamStr(c, "name") + me := c.MustGet("user").(*models.User) + + ginx.NewRender(c).Message(models.BuiltinCateDelete(rt.Ctx, name, me.Id)) +} + +func (rt *Router) alertRuleBuiltinList(c *gin.Context) { + fp := rt.Center.BuiltinIntegrationsDir + if fp == "" { + fp = path.Join(runner.Cwd, "etc", "alerts") + } + + files, err := file.FilesUnder(fp) + ginx.Dangerous(err) + + names := make([]string, 0, len(files)) + + for _, f := range files { + if !strings.HasSuffix(f, ".json") { + continue + } + + name := strings.TrimSuffix(f, ".json") + names = append(names, name) + } + + ginx.NewRender(c).Data(names, nil) +} + +type alertRuleBuiltinImportForm struct { + Name string `json:"name" binding:"required"` + DatasourceId int64 `json:"datasource_id" binding:"required"` +} + +func (rt *Router) alertRuleBuiltinImport(c *gin.Context) { + var f alertRuleBuiltinImportForm + ginx.BindJSON(c, &f) + + dirpath := rt.Center.BuiltinIntegrationsDir + if dirpath == "" { + dirpath = path.Join(runner.Cwd, "etc", "alerts") + } + + jsonfile := path.Join(dirpath, f.Name+".json") + if !file.IsExist(jsonfile) { + ginx.Bomb(http.StatusBadRequest, "%s not found", jsonfile) + } + + var lst []models.AlertRule + ginx.Dangerous(file.ReadJson(jsonfile, &lst)) + + count := len(lst) + if count == 0 { + ginx.Bomb(http.StatusBadRequest, "builtin alerts is empty, file: %s", jsonfile) + } + + username := c.MustGet("username").(string) + bgid := ginx.UrlParamInt64(c, "id") + + // alert rule name -> error string + reterr := make(map[string]string) + for i := 0; i < count; i++ { + lst[i].Id = 0 + lst[i].DatasourceIdsJson = []int64{f.DatasourceId} + lst[i].GroupId = bgid + lst[i].CreateBy = username + lst[i].UpdateBy = username + + if err := lst[i].FE2DB(); err != nil { + reterr[lst[i].Name] = i18n.Sprintf(c.GetHeader("X-Language"), err.Error()) + continue + } + + if err := lst[i].Add(rt.Ctx, models.GetChannelMap(rt.Ctx)); err != nil { + reterr[lst[i].Name] = i18n.Sprintf(c.GetHeader("X-Language"), err.Error()) + } else { + reterr[lst[i].Name] = "" + } + } + + ginx.NewRender(c).Data(reterr, nil) +} + +type Payload struct { + Cate string `json:"cate"` + Fname string `json:"fname"` + Name string `json:"name"` + Configs interface{} `json:"configs"` + Tags string `json:"tags"` +} + +type BoardCate struct { + Name string `json:"name"` + IconUrl string `json:"icon_url"` + Boards []Payload `json:"boards"` + Favorite bool `json:"favorite"` +} + +func (rt *Router) builtinBoardDetailGets(c *gin.Context) { + var payload Payload + ginx.BindJSON(c, &payload) + + fp := rt.Center.BuiltinIntegrationsDir + if fp == "" { + fp = path.Join(runner.Cwd, "integrations") + } + + fn := fp + "/" + payload.Cate + "/dashboards/" + payload.Fname + content, err := file.ReadBytes(fn) + ginx.Dangerous(err) + + err = json.Unmarshal(content, &payload) + ginx.NewRender(c).Data(payload, err) +} + +func (rt *Router) builtinBoardCateGets(c *gin.Context) { + fp := rt.Center.BuiltinIntegrationsDir + if fp == "" { + fp = path.Join(runner.Cwd, "integrations") + } + + me := c.MustGet("user").(*models.User) + buildinFavoritesMap, err := models.BuiltinCateGetByUserId(rt.Ctx, me.Id) + if err != nil { + logger.Warningf("get builtin favorites fail: %v", err) + } + + var boardCates []BoardCate + dirList, err := file.DirsUnder(fp) + ginx.Dangerous(err) + for _, dir := range dirList { + var boardCate BoardCate + boardCate.Name = dir + files, err := file.FilesUnder(fp + "/" + dir + "/dashboards") + ginx.Dangerous(err) + + var boards []Payload + for _, f := range files { + fn := fp + "/" + dir + "/dashboards/" + f + content, err := file.ReadBytes(fn) + if err != nil { + logger.Warningf("add board fail: %v", err) + continue + } + + var payload Payload + err = json.Unmarshal(content, &payload) + if err != nil { + logger.Warningf("add board:%s fail: %v", fn, err) + continue + } + payload.Cate = dir + payload.Fname = f + payload.Configs = "" + boards = append(boards, payload) + } + boardCate.Boards = boards + + if _, ok := buildinFavoritesMap[dir]; ok { + boardCate.Favorite = true + } + + iconFiles, _ := file.FilesUnder(fp + "/" + dir + "/icon") + if len(iconFiles) > 0 { + boardCate.IconUrl = fmt.Sprintf("/api/n9e/integrations/icon/%s/%s", dir, iconFiles[0]) + } + + boardCates = append(boardCates, boardCate) + } + ginx.NewRender(c).Data(boardCates, nil) +} + +func (rt *Router) builtinBoardGets(c *gin.Context) { + fp := rt.Center.BuiltinIntegrationsDir + if fp == "" { + fp = path.Join(runner.Cwd, "integrations") + } + + var fileList []string + dirList, err := file.DirsUnder(fp) + ginx.Dangerous(err) + for _, dir := range dirList { + files, err := file.FilesUnder(fp + "/" + dir + "/dashboards") + ginx.Dangerous(err) + fileList = append(fileList, files...) + } + + names := make([]string, 0, len(fileList)) + for _, f := range fileList { + if !strings.HasSuffix(f, ".json") { + continue + } + + name := strings.TrimSuffix(f, ".json") + names = append(names, name) + } + + ginx.NewRender(c).Data(names, nil) +} + +type AlertCate struct { + Name string `json:"name"` + IconUrl string `json:"icon_url"` + AlertRules []models.AlertRule `json:"alert_rules"` + Favorite bool `json:"favorite"` +} + +func (rt *Router) builtinAlertCateGets(c *gin.Context) { + fp := rt.Center.BuiltinIntegrationsDir + if fp == "" { + fp = path.Join(runner.Cwd, "integrations") + } + + me := c.MustGet("user").(*models.User) + buildinFavoritesMap, err := models.BuiltinCateGetByUserId(rt.Ctx, me.Id) + if err != nil { + logger.Warningf("get builtin favorites fail: %v", err) + } + + var alertCates []AlertCate + dirList, err := file.DirsUnder(fp) + ginx.Dangerous(err) + for _, dir := range dirList { + var alertCate AlertCate + alertCate.Name = dir + files, err := file.FilesUnder(fp + "/" + dir + "/alerts") + ginx.Dangerous(err) + + var alertRules []models.AlertRule + for _, f := range files { + fn := fp + "/" + dir + "/alerts/" + f + content, err := file.ReadBytes(fn) + if err != nil { + logger.Warningf("add board fail: %v", err) + continue + } + + var ars []models.AlertRule + err = json.Unmarshal(content, &ars) + if err != nil { + logger.Warningf("add board:%s fail: %v", fn, err) + continue + } + alertRules = append(alertRules, ars...) + } + alertCate.AlertRules = alertRules + iconFiles, _ := file.FilesUnder(fp + "/" + dir + "/icon") + if len(iconFiles) > 0 { + alertCate.IconUrl = fmt.Sprintf("/api/n9e/integrations/icon/%s/%s", dir, iconFiles[0]) + } + + if _, ok := buildinFavoritesMap[dir]; ok { + alertCate.Favorite = true + } + + alertCates = append(alertCates, alertCate) + } + ginx.NewRender(c).Data(alertCates, nil) +} + +// read the json file content +func (rt *Router) builtinBoardGet(c *gin.Context) { + name := ginx.UrlParamStr(c, "name") + dirpath := rt.Center.BuiltinIntegrationsDir + if dirpath == "" { + dirpath = path.Join(runner.Cwd, "integrations") + } + + dirList, err := file.DirsUnder(dirpath) + ginx.Dangerous(err) + for _, dir := range dirList { + jsonFile := dirpath + "/" + dir + "/dashboards/" + name + ".json" + if file.IsExist(jsonFile) { + body, err := file.ReadString(jsonFile) + ginx.NewRender(c).Data(body, err) + return + } + } + + ginx.Bomb(http.StatusBadRequest, "%s not found", name) +} diff --git a/src/webapi/router/router_busi_group.go b/center/router/router_busi_group.go similarity index 69% rename from src/webapi/router/router_busi_group.go rename to center/router/router_busi_group.go index dddfdc8ebf83765d1d410ddf6fb2a253159e5658..fa8ffa3bd8d33138092302a01792e8a14fab52a8 100644 --- a/src/webapi/router/router_busi_group.go +++ b/center/router/router_busi_group.go @@ -3,12 +3,12 @@ package router import ( "net/http" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/logger" "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" ) type busiGroupForm struct { @@ -18,7 +18,7 @@ type busiGroupForm struct { Members []models.BusiGroupMember `json:"members"` } -func busiGroupAdd(c *gin.Context) { +func (rt *Router) busiGroupAdd(c *gin.Context) { var f busiGroupForm ginx.BindJSON(c, &f) @@ -39,10 +39,10 @@ func busiGroupAdd(c *gin.Context) { } username := c.MustGet("username").(string) - ginx.Dangerous(models.BusiGroupAdd(f.Name, f.LabelEnable, f.LabelValue, f.Members, username)) + ginx.Dangerous(models.BusiGroupAdd(rt.Ctx, f.Name, f.LabelEnable, f.LabelValue, f.Members, username)) // 如果创建成功,拿着name去查,应该可以查到 - newbg, err := models.BusiGroupGet("name=?", f.Name) + newbg, err := models.BusiGroupGet(rt.Ctx, "name=?", f.Name) ginx.Dangerous(err) if newbg == nil { @@ -53,16 +53,16 @@ func busiGroupAdd(c *gin.Context) { ginx.NewRender(c).Data(newbg.Id, nil) } -func busiGroupPut(c *gin.Context) { +func (rt *Router) busiGroupPut(c *gin.Context) { var f busiGroupForm ginx.BindJSON(c, &f) username := c.MustGet("username").(string) targetbg := c.MustGet("busi_group").(*models.BusiGroup) - ginx.NewRender(c).Message(targetbg.Update(f.Name, f.LabelEnable, f.LabelValue, username)) + ginx.NewRender(c).Message(targetbg.Update(rt.Ctx, f.Name, f.LabelEnable, f.LabelValue, username)) } -func busiGroupMemberAdd(c *gin.Context) { +func (rt *Router) busiGroupMemberAdd(c *gin.Context) { var members []models.BusiGroupMember ginx.BindJSON(c, &members) @@ -75,10 +75,10 @@ func busiGroupMemberAdd(c *gin.Context) { } } - ginx.NewRender(c).Message(targetbg.AddMembers(members, username)) + ginx.NewRender(c).Message(targetbg.AddMembers(rt.Ctx, members, username)) } -func busiGroupMemberDel(c *gin.Context) { +func (rt *Router) busiGroupMemberDel(c *gin.Context) { var members []models.BusiGroupMember ginx.BindJSON(c, &members) @@ -91,14 +91,14 @@ func busiGroupMemberDel(c *gin.Context) { } } - ginx.NewRender(c).Message(targetbg.DelMembers(members, username)) + ginx.NewRender(c).Message(targetbg.DelMembers(rt.Ctx, members, username)) } -func busiGroupDel(c *gin.Context) { +func (rt *Router) busiGroupDel(c *gin.Context) { username := c.MustGet("username").(string) targetbg := c.MustGet("busi_group").(*models.BusiGroup) - err := targetbg.Del() + err := targetbg.Del(rt.Ctx) if err != nil { logger.Infof("busi_group_delete fail: operator=%s, group_name=%s error=%v", username, targetbg.Name, err) } else { @@ -109,26 +109,29 @@ func busiGroupDel(c *gin.Context) { } // 我是超管、或者我是业务组成员 -func busiGroupGets(c *gin.Context) { +func (rt *Router) busiGroupGets(c *gin.Context) { limit := ginx.QueryInt(c, "limit", defaultLimit) query := ginx.QueryStr(c, "query", "") all := ginx.QueryBool(c, "all", false) me := c.MustGet("user").(*models.User) - lst, err := me.BusiGroups(limit, query, all) + lst, err := me.BusiGroups(rt.Ctx, limit, query, all) + if len(lst) == 0 { + lst = []models.BusiGroup{} + } ginx.NewRender(c).Data(lst, err) } // 这个接口只有在活跃告警页面才调用,获取各个BG的活跃告警数量 -func busiGroupAlertingsGets(c *gin.Context) { +func (rt *Router) busiGroupAlertingsGets(c *gin.Context) { ids := ginx.QueryStr(c, "ids", "") - ret, err := models.AlertNumbers(str.IdsInt64(ids)) + ret, err := models.AlertNumbers(rt.Ctx, str.IdsInt64(ids)) ginx.NewRender(c).Data(ret, err) } -func busiGroupGet(c *gin.Context) { - bg := BusiGroup(ginx.UrlParamInt64(c, "id")) - ginx.Dangerous(bg.FillUserGroups()) +func (rt *Router) busiGroupGet(c *gin.Context) { + bg := BusiGroup(rt.Ctx, ginx.UrlParamInt64(c, "id")) + ginx.Dangerous(bg.FillUserGroups(rt.Ctx)) ginx.NewRender(c).Data(bg, nil) } diff --git a/src/webapi/router/router_chart_share.go b/center/router/router_chart_share.go similarity index 53% rename from src/webapi/router/router_chart_share.go rename to center/router/router_chart_share.go index f874677aecc9601afdb5b61f99131d7cc8571db1..0d2c28b8335f432d4915d4e51e828a9760c3aaac 100644 --- a/src/webapi/router/router_chart_share.go +++ b/center/router/router_chart_share.go @@ -3,26 +3,26 @@ package router import ( "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" ) -func chartShareGets(c *gin.Context) { +func (rt *Router) chartShareGets(c *gin.Context) { ids := ginx.QueryStr(c, "ids", "") - lst, err := models.ChartShareGetsByIds(str.IdsInt64(ids, ",")) + lst, err := models.ChartShareGetsByIds(rt.Ctx, str.IdsInt64(ids, ",")) ginx.NewRender(c).Data(lst, err) } type chartShareForm struct { - Configs string `json:"configs"` + DatasourceId int64 `json:"datasource_id"` + Configs string `json:"configs"` } -func chartShareAdd(c *gin.Context) { +func (rt *Router) chartShareAdd(c *gin.Context) { username := c.MustGet("username").(string) - cluster := MustGetCluster(c) var forms []chartShareForm ginx.BindJSON(c, &forms) @@ -32,12 +32,12 @@ func chartShareAdd(c *gin.Context) { for _, f := range forms { chart := models.ChartShare{ - Cluster: cluster, - Configs: f.Configs, - CreateBy: username, - CreateAt: now, + DatasourceId: f.DatasourceId, + Configs: f.Configs, + CreateBy: username, + CreateAt: now, } - ginx.Dangerous(chart.Add()) + ginx.Dangerous(chart.Add(rt.Ctx)) ids = append(ids, chart.Id) } diff --git a/center/router/router_config.go b/center/router/router_config.go new file mode 100644 index 0000000000000000000000000000000000000000..00633017565aaedda67aaee052b60d8634cd7713 --- /dev/null +++ b/center/router/router_config.go @@ -0,0 +1,64 @@ +package router + +import ( + "encoding/json" + + "github.com/ccfos/nightingale/v6/models" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) notifyChannelsGets(c *gin.Context) { + var labelAndKeys []models.LabelAndKey + cval, err := models.ConfigsGet(rt.Ctx, models.NOTIFYCHANNEL) + ginx.Dangerous(err) + + if cval == "" { + ginx.NewRender(c).Data(labelAndKeys, nil) + return + } + + var notifyChannels []models.NotifyChannel + err = json.Unmarshal([]byte(cval), ¬ifyChannels) + ginx.Dangerous(err) + + for _, v := range notifyChannels { + if v.Hide { + continue + } + var labelAndKey models.LabelAndKey + labelAndKey.Label = v.Name + labelAndKey.Key = v.Ident + labelAndKeys = append(labelAndKeys, labelAndKey) + } + + ginx.NewRender(c).Data(labelAndKeys, nil) +} + +func (rt *Router) contactKeysGets(c *gin.Context) { + var labelAndKeys []models.LabelAndKey + cval, err := models.ConfigsGet(rt.Ctx, models.NOTIFYCONTACT) + ginx.Dangerous(err) + + if cval == "" { + ginx.NewRender(c).Data(labelAndKeys, nil) + return + } + + var notifyContacts []models.NotifyContact + err = json.Unmarshal([]byte(cval), ¬ifyContacts) + ginx.Dangerous(err) + + for _, v := range notifyContacts { + if v.Hide { + continue + } + var labelAndKey models.LabelAndKey + labelAndKey.Label = v.Name + labelAndKey.Key = v.Ident + labelAndKeys = append(labelAndKeys, labelAndKey) + } + + ginx.NewRender(c).Data(labelAndKeys, nil) +} diff --git a/src/webapi/router/router_configs.go b/center/router/router_configs.go similarity index 51% rename from src/webapi/router/router_configs.go rename to center/router/router_configs.go index 83d30478172c2b98952fbe8b851e5385e1ba14bb..0a1194777e141269e5717b9cabdb205d0ddb755e 100644 --- a/src/webapi/router/router_configs.go +++ b/center/router/router_configs.go @@ -1,48 +1,48 @@ package router import ( - "github.com/didi/nightingale/v5/src/models" + "github.com/ccfos/nightingale/v6/models" "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" ) -func configsGet(c *gin.Context) { +func (rt *Router) configsGet(c *gin.Context) { prefix := ginx.QueryStr(c, "prefix", "") limit := ginx.QueryInt(c, "limit", 10) - configs, err := models.ConfigsGets(prefix, limit, ginx.Offset(c, limit)) + configs, err := models.ConfigsGets(rt.Ctx, prefix, limit, ginx.Offset(c, limit)) ginx.NewRender(c).Data(configs, err) } -func configGet(c *gin.Context) { +func (rt *Router) configGet(c *gin.Context) { id := ginx.UrlParamInt64(c, "id") - configs, err := models.ConfigGet(id) + configs, err := models.ConfigGet(rt.Ctx, id) ginx.NewRender(c).Data(configs, err) } -func configsDel(c *gin.Context) { +func (rt *Router) configsDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) - ginx.NewRender(c).Message(models.ConfigsDel(f.Ids)) + ginx.NewRender(c).Message(models.ConfigsDel(rt.Ctx, f.Ids)) } -func configsPut(c *gin.Context) { +func (rt *Router) configsPut(c *gin.Context) { var arr []models.Configs ginx.BindJSON(c, &arr) for i := 0; i < len(arr); i++ { - ginx.Dangerous(arr[i].Update()) + ginx.Dangerous(arr[i].Update(rt.Ctx)) } ginx.NewRender(c).Message(nil) } -func configsPost(c *gin.Context) { +func (rt *Router) configsPost(c *gin.Context) { var arr []models.Configs ginx.BindJSON(c, &arr) for i := 0; i < len(arr); i++ { - ginx.Dangerous(arr[i].Add()) + ginx.Dangerous(arr[i].Add(rt.Ctx)) } ginx.NewRender(c).Message(nil) diff --git a/src/webapi/router/router_crypto.go b/center/router/router_crypto.go similarity index 86% rename from src/webapi/router/router_crypto.go rename to center/router/router_crypto.go index 3ecd9185248bc89f0167ff1cbc44a7a838ead1c0..9a5e0dd626570902a29de2c8500291b8621037ba 100644 --- a/src/webapi/router/router_crypto.go +++ b/center/router/router_crypto.go @@ -1,7 +1,8 @@ package router import ( - "github.com/didi/nightingale/v5/src/pkg/secu" + "github.com/ccfos/nightingale/v6/pkg/secu" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" ) @@ -11,7 +12,7 @@ type confPropCrypto struct { Key string `json:"key" binding:"required"` } -func confPropEncrypt(c *gin.Context) { +func (rt *Router) confPropEncrypt(c *gin.Context) { var f confPropCrypto ginx.BindJSON(c, &f) @@ -36,7 +37,7 @@ func confPropEncrypt(c *gin.Context) { }) } -func confPropDecrypt(c *gin.Context) { +func (rt *Router) confPropDecrypt(c *gin.Context) { var f confPropCrypto ginx.BindJSON(c, &f) diff --git a/center/router/router_dashboard.go b/center/router/router_dashboard.go new file mode 100644 index 0000000000000000000000000000000000000000..51cf2da47a79a151def95a1db94fd6d8ff676c52 --- /dev/null +++ b/center/router/router_dashboard.go @@ -0,0 +1,19 @@ +package router + +type ChartPure struct { + Configs string `json:"configs"` + Weight int `json:"weight"` +} + +type ChartGroupPure struct { + Name string `json:"name"` + Weight int `json:"weight"` + Charts []ChartPure `json:"charts"` +} + +type DashboardPure struct { + Name string `json:"name"` + Tags string `json:"tags"` + Configs string `json:"configs"` + ChartGroups []ChartGroupPure `json:"chart_groups"` +} diff --git a/center/router/router_datasource.go b/center/router/router_datasource.go new file mode 100644 index 0000000000000000000000000000000000000000..44a2002aff9bcabd84b9086976d6ee54bbd860aa --- /dev/null +++ b/center/router/router_datasource.go @@ -0,0 +1,97 @@ +package router + +import ( + "github.com/ccfos/nightingale/v6/models" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) pluginList(c *gin.Context) { + Render(c, rt.Center.Plugins, nil) +} + +type listReq struct { + Name string `json:"name"` + Type string `json:"plugin_type"` + Category string `json:"category"` + Limit int `json:"limit"` + P int `json:"p"` +} + +func (rt *Router) datasourceList(c *gin.Context) { + var req listReq + ginx.BindJSON(c, &req) + if req.Limit == 0 { + req.Limit = 10 + } + if req.P == 0 { + req.P = 1 + } + + typ := req.Type + category := req.Category + name := req.Name + limit := req.Limit + p := req.P + + list, err := models.GetDatasourcesGetsBy(rt.Ctx, typ, category, name, limit, (p-1)*limit) + Render(c, list, err) +} + +func (rt *Router) datasourceUpsert(c *gin.Context) { + var req models.Datasource + ginx.BindJSON(c, &req) + username := Username(c) + req.UpdatedBy = username + + var err error + var count int64 + if req.Id == 0 { + req.CreatedBy = username + req.Status = "enabled" + count, err = models.GetDatasourcesCountBy(rt.Ctx, "", "", req.Name) + if err != nil { + Render(c, nil, err) + return + } + + if count > 0 { + Render(c, nil, "name already exists") + return + } + err = req.Add(rt.Ctx) + } else { + err = req.Update(rt.Ctx, "name", "description", "cluster_name", "settings", "http", "auth", "status", "updated_by", "updated_at") + } + + Render(c, nil, err) +} + +func (rt *Router) datasourceGet(c *gin.Context) { + var req models.Datasource + ginx.BindJSON(c, &req) + err := req.Get(rt.Ctx) + Render(c, req, err) +} + +func (rt *Router) datasourceUpdataStatus(c *gin.Context) { + var req models.Datasource + ginx.BindJSON(c, &req) + username := Username(c) + req.UpdatedBy = username + err := req.Update(rt.Ctx, "status", "updated_by", "updated_at") + Render(c, req, err) +} + +func (rt *Router) datasourceDel(c *gin.Context) { + var ids []int64 + ginx.BindJSON(c, &ids) + err := models.DatasourceDel(rt.Ctx, ids) + Render(c, nil, err) +} + +func Username(c *gin.Context) string { + + return c.MustGet("username").(string) +} diff --git a/center/router/router_funcs.go b/center/router/router_funcs.go new file mode 100644 index 0000000000000000000000000000000000000000..dc439cf4a39db0b999da29ae3cb74eaf90027180 --- /dev/null +++ b/center/router/router_funcs.go @@ -0,0 +1,121 @@ +package router + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/ccfos/nightingale/v6/center/cconf" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/ibex" + "github.com/gin-gonic/gin" + + "github.com/toolkits/pkg/ginx" +) + +const defaultLimit = 300 + +func queryDatasourceIds(c *gin.Context) []int64 { + datasourceIds := ginx.QueryStr(c, "datasource_ids", "") + datasourceIds = strings.ReplaceAll(datasourceIds, ",", " ") + idsStr := strings.Fields(datasourceIds) + ids := make([]int64, len(idsStr)) + for i, idStr := range idsStr { + id, _ := strconv.ParseInt(idStr, 10, 64) + ids[i] = id + } + return ids +} + +type idsForm struct { + Ids []int64 `json:"ids"` +} + +func (f idsForm) Verify() { + if len(f.Ids) == 0 { + ginx.Bomb(http.StatusBadRequest, "ids empty") + } +} + +func User(ctx *ctx.Context, id int64) *models.User { + obj, err := models.UserGetById(ctx, id) + ginx.Dangerous(err) + + if obj == nil { + ginx.Bomb(http.StatusNotFound, "No such user") + } + + return obj +} + +func UserGroup(ctx *ctx.Context, id int64) *models.UserGroup { + obj, err := models.UserGroupGetById(ctx, id) + ginx.Dangerous(err) + + if obj == nil { + ginx.Bomb(http.StatusNotFound, "No such UserGroup") + } + + return obj +} + +func BusiGroup(ctx *ctx.Context, id int64) *models.BusiGroup { + obj, err := models.BusiGroupGetById(ctx, id) + ginx.Dangerous(err) + + if obj == nil { + ginx.Bomb(http.StatusNotFound, "No such BusiGroup") + } + + return obj +} + +func Dashboard(ctx *ctx.Context, id int64) *models.Dashboard { + obj, err := models.DashboardGet(ctx, "id=?", id) + ginx.Dangerous(err) + + if obj == nil { + ginx.Bomb(http.StatusNotFound, "No such dashboard") + } + + return obj +} + +type DoneIdsReply struct { + Err string `json:"err"` + Dat struct { + List []int64 `json:"list"` + } `json:"dat"` +} + +type TaskCreateReply struct { + Err string `json:"err"` + Dat int64 `json:"dat"` // task.id +} + +// return task.id, error +func TaskCreate(v interface{}, ibexc cconf.Ibex) (int64, error) { + var res TaskCreateReply + err := ibex.New( + ibexc.Address, + ibexc.BasicAuthUser, + ibexc.BasicAuthPass, + ibexc.Timeout, + ). + Path("/ibex/v1/tasks"). + In(v). + Out(&res). + POST() + + if err != nil { + return 0, err + } + + if res.Err != "" { + return 0, fmt.Errorf("response.err: %v", res.Err) + } + + return res.Dat, nil +} diff --git a/src/webapi/router/router_login.go b/center/router/router_login.go similarity index 64% rename from src/webapi/router/router_login.go rename to center/router/router_login.go index 37aa10e15a8e0ce99810d7a131f9b46d28463a86..d960b0c452c141c8455dc7c39ea9c1da55ad5570 100644 --- a/src/webapi/router/router_login.go +++ b/center/router/router_login.go @@ -7,16 +7,17 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/cas" + "github.com/ccfos/nightingale/v6/pkg/ldapx" + "github.com/ccfos/nightingale/v6/pkg/oauth2x" + "github.com/ccfos/nightingale/v6/pkg/oidcx" + "github.com/pelletier/go-toml/v2" + "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/cas" - "github.com/didi/nightingale/v5/src/pkg/oauth2x" - "github.com/didi/nightingale/v5/src/pkg/oidcc" - "github.com/didi/nightingale/v5/src/webapi/config" ) type loginForm struct { @@ -24,20 +25,22 @@ type loginForm struct { Password string `json:"password" binding:"required"` } -func loginPost(c *gin.Context) { +func (rt *Router) loginPost(c *gin.Context) { var f loginForm ginx.BindJSON(c, &f) - user, err := models.PassLogin(f.Username, f.Password) + user, err := models.PassLogin(rt.Ctx, f.Username, f.Password) if err != nil { // pass validate fail, try ldap - if config.C.LDAP.Enable { - user, err = models.LdapLogin(f.Username, f.Password) + if rt.Center.LDAP.Enable { + roles := strings.Join(rt.Center.LDAP.DefaultRoles, " ") + user, err = models.LdapLogin(rt.Ctx, f.Username, f.Password, roles, rt.Sso.LDAP) if err != nil { logger.Debugf("ldap login failed: %v username: %s", err, f.Username) ginx.NewRender(c).Message(err) return } + user.RolesLst = rt.Center.LDAP.DefaultRoles } else { ginx.NewRender(c).Message(err) return @@ -52,9 +55,9 @@ func loginPost(c *gin.Context) { userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username) - ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity) + ts, err := rt.createTokens(rt.Center.JWTAuth.SigningKey, userIdentity) ginx.Dangerous(err) - ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts)) + ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts)) ginx.NewRender(c).Data(gin.H{ "user": user, @@ -63,14 +66,14 @@ func loginPost(c *gin.Context) { }, nil) } -func logoutPost(c *gin.Context) { - metadata, err := extractTokenMetadata(c.Request) +func (rt *Router) logoutPost(c *gin.Context) { + metadata, err := rt.extractTokenMetadata(c.Request) if err != nil { ginx.NewRender(c, http.StatusBadRequest).Message("failed to parse jwt token") return } - delErr := deleteTokens(c.Request.Context(), metadata) + delErr := rt.deleteTokens(c.Request.Context(), metadata) if delErr != nil { ginx.NewRender(c).Message(http.StatusText(http.StatusInternalServerError)) return @@ -83,7 +86,7 @@ type refreshForm struct { RefreshToken string `json:"refresh_token" binding:"required"` } -func refreshPost(c *gin.Context) { +func (rt *Router) refreshPost(c *gin.Context) { var f refreshForm ginx.BindJSON(c, &f) @@ -92,7 +95,7 @@ func refreshPost(c *gin.Context) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected jwt signing method: %v", token.Header["alg"]) } - return []byte(config.C.JWTAuth.SigningKey), nil + return []byte(rt.Center.JWTAuth.SigningKey), nil }) // if there is an error, the token must have expired @@ -125,7 +128,7 @@ func refreshPost(c *gin.Context) { return } - u, err := models.UserGetById(userid) + u, err := models.UserGetById(rt.Ctx, userid) if err != nil { ginx.NewRender(c, http.StatusInternalServerError).Message("failed to query user by id") return @@ -138,19 +141,19 @@ func refreshPost(c *gin.Context) { } // Delete the previous Refresh Token - err = deleteAuth(c.Request.Context(), refreshUuid) + err = rt.deleteAuth(c.Request.Context(), refreshUuid) if err != nil { ginx.NewRender(c, http.StatusUnauthorized).Message(http.StatusText(http.StatusInternalServerError)) return } // Delete previous Access Token - deleteAuth(c.Request.Context(), strings.Split(refreshUuid, "++")[0]) + rt.deleteAuth(c.Request.Context(), strings.Split(refreshUuid, "++")[0]) // Create new pairs of refresh and access tokens - ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity) + ts, err := rt.createTokens(rt.Center.JWTAuth.SigningKey, userIdentity) ginx.Dangerous(err) - ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts)) + ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts)) ginx.NewRender(c).Data(gin.H{ "access_token": ts.AccessToken, @@ -162,13 +165,13 @@ func refreshPost(c *gin.Context) { } } -func loginRedirect(c *gin.Context) { +func (rt *Router) loginRedirect(c *gin.Context) { redirect := ginx.QueryStr(c, "redirect", "/") v, exists := c.Get("userid") if exists { userid := v.(int64) - user, err := models.UserGetById(userid) + user, err := models.UserGetById(rt.Ctx, userid) ginx.Dangerous(err) if user == nil { ginx.Bomb(200, "user not found") @@ -180,12 +183,12 @@ func loginRedirect(c *gin.Context) { } } - if !config.C.OIDC.Enable { + if !rt.Center.OIDC.Enable { ginx.NewRender(c).Data("", nil) return } - redirect, err := oidcc.Authorize(redirect) + redirect, err := rt.Sso.OIDC.Authorize(rt.Redis, redirect) ginx.Dangerous(err) ginx.NewRender(c).Data(redirect, err) @@ -198,22 +201,22 @@ type CallbackOutput struct { RefreshToken string `json:"refresh_token"` } -func loginCallback(c *gin.Context) { +func (rt *Router) loginCallback(c *gin.Context) { code := ginx.QueryStr(c, "code", "") state := ginx.QueryStr(c, "state", "") - ret, err := oidcc.Callback(c.Request.Context(), code, state) + ret, err := rt.Sso.OIDC.Callback(rt.Redis, c.Request.Context(), code, state) if err != nil { logger.Debugf("sso.callback() get ret %+v error %v", ret, err) ginx.NewRender(c).Data(CallbackOutput{}, err) return } - user, err := models.UserGet("username=?", ret.Username) + user, err := models.UserGet(rt.Ctx, "username=?", ret.Username) ginx.Dangerous(err) if user != nil { - if config.C.OIDC.CoverAttributes { + if rt.Center.OIDC.CoverAttributes { if ret.Nickname != "" { user.Nickname = ret.Nickname } @@ -227,7 +230,7 @@ func loginCallback(c *gin.Context) { } user.UpdateAt = time.Now().Unix() - user.Update("email", "nickname", "phone", "update_at") + user.Update(rt.Ctx, "email", "nickname", "phone", "update_at") } } else { now := time.Now().Unix() @@ -238,8 +241,8 @@ func loginCallback(c *gin.Context) { Phone: ret.Phone, Email: ret.Email, Portrait: "", - Roles: strings.Join(config.C.OIDC.DefaultRoles, " "), - RolesLst: config.C.OIDC.DefaultRoles, + Roles: strings.Join(rt.Center.OIDC.DefaultRoles, " "), + RolesLst: rt.Center.OIDC.DefaultRoles, Contacts: []byte("{}"), CreateAt: now, UpdateAt: now, @@ -248,14 +251,14 @@ func loginCallback(c *gin.Context) { } // create user from oidc - ginx.Dangerous(user.Add()) + ginx.Dangerous(user.Add(rt.Ctx)) } // set user login state userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username) - ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity) + ts, err := rt.createTokens(rt.Center.JWTAuth.SigningKey, userIdentity) ginx.Dangerous(err) - ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts)) + ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts)) redirect := "/" if ret.Redirect != "/login" { @@ -275,13 +278,13 @@ type RedirectOutput struct { State string `json:"state"` } -func loginRedirectCas(c *gin.Context) { +func (rt *Router) loginRedirectCas(c *gin.Context) { redirect := ginx.QueryStr(c, "redirect", "/") v, exists := c.Get("userid") if exists { userid := v.(int64) - user, err := models.UserGetById(userid) + user, err := models.UserGetById(rt.Ctx, userid) ginx.Dangerous(err) if user == nil { ginx.Bomb(200, "user not found") @@ -293,13 +296,13 @@ func loginRedirectCas(c *gin.Context) { } } - if !config.C.CAS.Enable { + if !rt.Center.CAS.Enable { logger.Error("cas is not enable") ginx.NewRender(c).Data("", nil) return } - redirect, state, err := cas.Authorize(redirect) + redirect, state, err := rt.Sso.CAS.Authorize(rt.Redis, redirect) ginx.Dangerous(err) ginx.NewRender(c).Data(RedirectOutput{ @@ -308,22 +311,22 @@ func loginRedirectCas(c *gin.Context) { }, err) } -func loginCallbackCas(c *gin.Context) { +func (rt *Router) loginCallbackCas(c *gin.Context) { ticket := ginx.QueryStr(c, "ticket", "") state := ginx.QueryStr(c, "state", "") - ret, err := cas.ValidateServiceTicket(c.Request.Context(), ticket, state) + ret, err := rt.Sso.CAS.ValidateServiceTicket(c.Request.Context(), ticket, state, rt.Redis) if err != nil { logger.Errorf("ValidateServiceTicket: %s", err) ginx.NewRender(c).Data("", err) return } - user, err := models.UserGet("username=?", ret.Username) + user, err := models.UserGet(rt.Ctx, "username=?", ret.Username) if err != nil { logger.Errorf("UserGet: %s", err) } ginx.Dangerous(err) if user != nil { - if config.C.CAS.CoverAttributes { + if rt.Center.CAS.CoverAttributes { if ret.Nickname != "" { user.Nickname = ret.Nickname } @@ -337,7 +340,7 @@ func loginCallbackCas(c *gin.Context) { } user.UpdateAt = time.Now().Unix() - ginx.Dangerous(user.Update("email", "nickname", "phone", "update_at")) + ginx.Dangerous(user.Update(rt.Ctx, "email", "nickname", "phone", "update_at")) } } else { now := time.Now().Unix() @@ -346,8 +349,8 @@ func loginCallbackCas(c *gin.Context) { Password: "******", Nickname: ret.Nickname, Portrait: "", - Roles: strings.Join(config.C.CAS.DefaultRoles, " "), - RolesLst: config.C.CAS.DefaultRoles, + Roles: strings.Join(rt.Center.CAS.DefaultRoles, " "), + RolesLst: rt.Center.CAS.DefaultRoles, Contacts: []byte("{}"), Phone: ret.Phone, Email: ret.Email, @@ -357,17 +360,17 @@ func loginCallbackCas(c *gin.Context) { UpdateBy: "CAS", } // create user from cas - ginx.Dangerous(user.Add()) + ginx.Dangerous(user.Add(rt.Ctx)) } // set user login state userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username) - ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity) + ts, err := rt.createTokens(rt.Center.JWTAuth.SigningKey, userIdentity) if err != nil { logger.Errorf("createTokens: %s", err) } ginx.Dangerous(err) - ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts)) + ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts)) redirect := "/" if ret.Redirect != "/login" { @@ -381,13 +384,13 @@ func loginCallbackCas(c *gin.Context) { }, nil) } -func loginRedirectOAuth(c *gin.Context) { +func (rt *Router) loginRedirectOAuth(c *gin.Context) { redirect := ginx.QueryStr(c, "redirect", "/") v, exists := c.Get("userid") if exists { userid := v.(int64) - user, err := models.UserGetById(userid) + user, err := models.UserGetById(rt.Ctx, userid) ginx.Dangerous(err) if user == nil { ginx.Bomb(200, "user not found") @@ -399,33 +402,33 @@ func loginRedirectOAuth(c *gin.Context) { } } - if !config.C.OAuth.Enable { + if !rt.Center.OAuth.Enable { ginx.NewRender(c).Data("", nil) return } - redirect, err := oauth2x.Authorize(redirect) + redirect, err := rt.Sso.OAuth2.Authorize(rt.Redis, redirect) ginx.Dangerous(err) ginx.NewRender(c).Data(redirect, err) } -func loginCallbackOAuth(c *gin.Context) { +func (rt *Router) loginCallbackOAuth(c *gin.Context) { code := ginx.QueryStr(c, "code", "") state := ginx.QueryStr(c, "state", "") - ret, err := oauth2x.Callback(c.Request.Context(), code, state) + ret, err := rt.Sso.OAuth2.Callback(rt.Redis, c.Request.Context(), code, state) if err != nil { logger.Debugf("sso.callback() get ret %+v error %v", ret, err) ginx.NewRender(c).Data(CallbackOutput{}, err) return } - user, err := models.UserGet("username=?", ret.Username) + user, err := models.UserGet(rt.Ctx, "username=?", ret.Username) ginx.Dangerous(err) if user != nil { - if config.C.OAuth.CoverAttributes { + if rt.Center.OAuth.CoverAttributes { if ret.Nickname != "" { user.Nickname = ret.Nickname } @@ -439,7 +442,7 @@ func loginCallbackOAuth(c *gin.Context) { } user.UpdateAt = time.Now().Unix() - user.Update("email", "nickname", "phone", "update_at") + user.Update(rt.Ctx, "email", "nickname", "phone", "update_at") } } else { now := time.Now().Unix() @@ -450,8 +453,8 @@ func loginCallbackOAuth(c *gin.Context) { Phone: ret.Phone, Email: ret.Email, Portrait: "", - Roles: strings.Join(config.C.OAuth.DefaultRoles, " "), - RolesLst: config.C.OAuth.DefaultRoles, + Roles: strings.Join(rt.Center.OAuth.DefaultRoles, " "), + RolesLst: rt.Center.OAuth.DefaultRoles, Contacts: []byte("{}"), CreateAt: now, UpdateAt: now, @@ -460,14 +463,14 @@ func loginCallbackOAuth(c *gin.Context) { } // create user from oidc - ginx.Dangerous(user.Add()) + ginx.Dangerous(user.Add(rt.Ctx)) } // set user login state userIdentity := fmt.Sprintf("%d-%s", user.Id, user.Username) - ts, err := createTokens(config.C.JWTAuth.SigningKey, userIdentity) + ts, err := rt.createTokens(rt.Center.JWTAuth.SigningKey, userIdentity) ginx.Dangerous(err) - ginx.Dangerous(createAuth(c.Request.Context(), userIdentity, ts)) + ginx.Dangerous(rt.createAuth(c.Request.Context(), userIdentity, ts)) redirect := "/" if ret.Redirect != "/login" { @@ -488,10 +491,49 @@ type SsoConfigOutput struct { OauthDisplayName string `json:"oauthDisplayName"` } -func ssoConfigGet(c *gin.Context) { +func (rt *Router) ssoConfigNameGet(c *gin.Context) { ginx.NewRender(c).Data(SsoConfigOutput{ - OidcDisplayName: oidcc.GetDisplayName(), - CasDisplayName: cas.GetDisplayName(), - OauthDisplayName: oauth2x.GetDisplayName(), + OidcDisplayName: rt.Sso.OIDC.GetDisplayName(), + CasDisplayName: rt.Sso.CAS.GetDisplayName(), + OauthDisplayName: rt.Sso.OAuth2.GetDisplayName(), }, nil) } + +func (rt *Router) ssoConfigGets(c *gin.Context) { + ginx.NewRender(c).Data(models.SsoConfigGets(rt.Ctx)) +} + +func (rt *Router) ssoConfigUpdate(c *gin.Context) { + var f models.SsoConfig + ginx.BindJSON(c, &f) + + err := f.Update(rt.Ctx) + ginx.Dangerous(err) + + switch f.Name { + case "LDAP": + var config ldapx.Config + err := toml.Unmarshal([]byte(f.Content), &config) + ginx.Dangerous(err) + rt.Sso.LDAP.Reload(config) + case "OIDC": + var config oidcx.Config + err := toml.Unmarshal([]byte(f.Content), &config) + ginx.Dangerous(err) + + err = rt.Sso.OIDC.Reload(config) + ginx.Bomb(200, "oidc init error: %v", err) + case "CAS": + var config cas.Config + err := toml.Unmarshal([]byte(f.Content), &config) + ginx.Dangerous(err) + rt.Sso.CAS.Reload(config) + case "OAuth2": + var config oauth2x.Config + err := toml.Unmarshal([]byte(f.Content), &config) + ginx.Dangerous(err) + rt.Sso.OAuth2.Reload(config) + } + + ginx.NewRender(c).Message(nil) +} diff --git a/src/webapi/router/router_metric_desc.go b/center/router/router_metric_desc.go similarity index 89% rename from src/webapi/router/router_metric_desc.go rename to center/router/router_metric_desc.go index f11f45f07d9bdfc9bc786ea8560207d6a7681b73..61e918b6551e985e1496f2a64fa1d34f815bed2c 100644 --- a/src/webapi/router/router_metric_desc.go +++ b/center/router/router_metric_desc.go @@ -1,24 +1,24 @@ package router import ( + "github.com/ccfos/nightingale/v6/center/cconf" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/webapi/config" ) -func metricsDescGetFile(c *gin.Context) { - c.JSON(200, config.MetricDesc) +func (rt *Router) metricsDescGetFile(c *gin.Context) { + c.JSON(200, rt.Center.MetricDesc) } // 前端传过来一个metric数组,后端去查询有没有对应的释义,返回map -func metricsDescGetMap(c *gin.Context) { +func (rt *Router) metricsDescGetMap(c *gin.Context) { var arr []string ginx.BindJSON(c, &arr) ret := make(map[string]string) for _, key := range arr { - ret[key] = config.GetMetricDesc(c.GetHeader("X-Language"), key) + ret[key] = cconf.GetMetricDesc(c.GetHeader("X-Language"), key) } ginx.NewRender(c).Data(ret, nil) diff --git a/src/webapi/router/router_metric_view.go b/center/router/router_metric_view.go similarity index 63% rename from src/webapi/router/router_metric_view.go rename to center/router/router_metric_view.go index b0253f20e97bfdcb49cd0d043444fe1c0dc17dbc..c4442367b1f2d16f91f2a8df61381e466d13b7e1 100644 --- a/src/webapi/router/router_metric_view.go +++ b/center/router/router_metric_view.go @@ -3,19 +3,20 @@ package router import ( "net/http" - "github.com/didi/nightingale/v5/src/models" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" ) // no param -func metricViewGets(c *gin.Context) { - lst, err := models.MetricViewGets(c.MustGet("userid")) +func (rt *Router) metricViewGets(c *gin.Context) { + lst, err := models.MetricViewGets(rt.Ctx, c.MustGet("userid")) ginx.NewRender(c).Data(lst, err) } // body: name, configs, cate -func metricViewAdd(c *gin.Context) { +func (rt *Router) metricViewAdd(c *gin.Context) { var f models.MetricView ginx.BindJSON(c, &f) @@ -28,31 +29,31 @@ func metricViewAdd(c *gin.Context) { f.Id = 0 f.CreateBy = me.Id - ginx.Dangerous(f.Add()) + ginx.Dangerous(f.Add(rt.Ctx)) ginx.NewRender(c).Data(f, nil) } // body: ids -func metricViewDel(c *gin.Context) { +func (rt *Router) metricViewDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() me := c.MustGet("user").(*models.User) if me.IsAdmin() { - ginx.NewRender(c).Message(models.MetricViewDel(f.Ids)) + ginx.NewRender(c).Message(models.MetricViewDel(rt.Ctx, f.Ids)) } else { - ginx.NewRender(c).Message(models.MetricViewDel(f.Ids, me.Id)) + ginx.NewRender(c).Message(models.MetricViewDel(rt.Ctx, f.Ids, me.Id)) } } // body: id, name, configs, cate -func metricViewPut(c *gin.Context) { +func (rt *Router) metricViewPut(c *gin.Context) { var f models.MetricView ginx.BindJSON(c, &f) - view, err := models.MetricViewGet("id = ?", f.Id) + view, err := models.MetricViewGet(rt.Ctx, "id = ?", f.Id) ginx.Dangerous(err) if view == nil { @@ -71,5 +72,5 @@ func metricViewPut(c *gin.Context) { } } - ginx.NewRender(c).Message(view.Update(f.Name, f.Configs, f.Cate, me.Id)) + ginx.NewRender(c).Message(view.Update(rt.Ctx, f.Name, f.Configs, f.Cate, me.Id)) } diff --git a/src/webapi/router/router_mute.go b/center/router/router_mute.go similarity index 60% rename from src/webapi/router/router_mute.go rename to center/router/router_mute.go index d2eb0b34c5aed53325a2618b09ac3753aadff6e7..d5ffb6499ce6e17f8b0ce2636998750c502ac1b7 100644 --- a/src/webapi/router/router_mute.go +++ b/center/router/router_mute.go @@ -5,29 +5,30 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" ) // Return all, front-end search and paging -func alertMuteGetsByBG(c *gin.Context) { +func (rt *Router) alertMuteGetsByBG(c *gin.Context) { bgid := ginx.UrlParamInt64(c, "id") - lst, err := models.AlertMuteGetsByBG(bgid) + lst, err := models.AlertMuteGetsByBG(rt.Ctx, bgid) + ginx.NewRender(c).Data(lst, err) } -func alertMuteGets(c *gin.Context) { +func (rt *Router) alertMuteGets(c *gin.Context) { prods := strings.Fields(ginx.QueryStr(c, "prods", "")) bgid := ginx.QueryInt64(c, "bgid", 0) query := ginx.QueryStr(c, "query", "") - lst, err := models.AlertMuteGets(prods, bgid, query) + lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, query) ginx.NewRender(c).Data(lst, err) } -func alertMuteAdd(c *gin.Context) { +func (rt *Router) alertMuteAdd(c *gin.Context) { var f models.AlertMute ginx.BindJSON(c, &f) @@ -35,30 +36,30 @@ func alertMuteAdd(c *gin.Context) { f.CreateBy = username f.GroupId = ginx.UrlParamInt64(c, "id") - ginx.NewRender(c).Message(f.Add()) + ginx.NewRender(c).Message(f.Add(rt.Ctx)) } -func alertMuteAddByService(c *gin.Context) { +func (rt *Router) alertMuteAddByService(c *gin.Context) { var f models.AlertMute ginx.BindJSON(c, &f) - ginx.NewRender(c).Message(f.Add()) + ginx.NewRender(c).Message(f.Add(rt.Ctx)) } -func alertMuteDel(c *gin.Context) { +func (rt *Router) alertMuteDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() - ginx.NewRender(c).Message(models.AlertMuteDel(f.Ids)) + ginx.NewRender(c).Message(models.AlertMuteDel(rt.Ctx, f.Ids)) } -func alertMutePutByFE(c *gin.Context) { +func (rt *Router) alertMutePutByFE(c *gin.Context) { var f models.AlertMute ginx.BindJSON(c, &f) amid := ginx.UrlParamInt64(c, "amid") - am, err := models.AlertMuteGetById(amid) + am, err := models.AlertMuteGetById(rt.Ctx, amid) ginx.Dangerous(err) if am == nil { @@ -66,10 +67,10 @@ func alertMutePutByFE(c *gin.Context) { return } - bgrwCheck(c, am.GroupId) + rt.bgrwCheck(c, am.GroupId) f.UpdateBy = c.MustGet("username").(string) - ginx.NewRender(c).Message(am.Update(f)) + ginx.NewRender(c).Message(am.Update(rt.Ctx, f)) } type alertMuteFieldForm struct { @@ -77,7 +78,7 @@ type alertMuteFieldForm struct { Fields map[string]interface{} `json:"fields"` } -func alertMutePutFields(c *gin.Context) { +func (rt *Router) alertMutePutFields(c *gin.Context) { var f alertMuteFieldForm ginx.BindJSON(c, &f) @@ -89,14 +90,15 @@ func alertMutePutFields(c *gin.Context) { f.Fields["update_at"] = time.Now().Unix() for i := 0; i < len(f.Ids); i++ { - am, err := models.AlertMuteGetById(f.Ids[i]) + am, err := models.AlertMuteGetById(rt.Ctx, f.Ids[i]) ginx.Dangerous(err) if am == nil { continue } - ginx.Dangerous(am.UpdateFieldsMap(f.Fields)) + am.FE2DB() + ginx.Dangerous(am.UpdateFieldsMap(rt.Ctx, f.Fields)) } ginx.NewRender(c).Message(nil) diff --git a/src/webapi/router/router_mw.go b/center/router/router_mw.go similarity index 63% rename from src/webapi/router/router_mw.go rename to center/router/router_mw.go index 8bb8035faf35455f07430d92db89248ff904fd46..a428fc2d72f7ba563f0fafa9c0ef4bd10a20c29d 100644 --- a/src/webapi/router/router_mw.go +++ b/center/router/router_mw.go @@ -9,14 +9,12 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "github.com/google/uuid" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/storage" - "github.com/didi/nightingale/v5/src/webapi/config" ) type AccessDetails struct { @@ -24,14 +22,14 @@ type AccessDetails struct { UserIdentity string } -func handleProxyUser(c *gin.Context) *models.User { - headerUserNameKey := config.C.ProxyAuth.HeaderUserNameKey +func (rt *Router) handleProxyUser(c *gin.Context) *models.User { + headerUserNameKey := rt.Center.ProxyAuth.HeaderUserNameKey username := c.GetHeader(headerUserNameKey) if username == "" { ginx.Bomb(http.StatusUnauthorized, "unauthorized") } - user, err := models.UserGetByUsername(username) + user, err := models.UserGetByUsername(rt.Ctx, username) if err != nil { ginx.Bomb(http.StatusInternalServerError, err.Error()) } @@ -41,13 +39,13 @@ func handleProxyUser(c *gin.Context) *models.User { user = &models.User{ Username: username, Nickname: username, - Roles: strings.Join(config.C.ProxyAuth.DefaultRoles, " "), + Roles: strings.Join(rt.Center.ProxyAuth.DefaultRoles, " "), CreateAt: now, UpdateAt: now, CreateBy: "system", UpdateBy: "system", } - err = user.Add() + err = user.Add(rt.Ctx) if err != nil { ginx.Bomb(http.StatusInternalServerError, err.Error()) } @@ -55,23 +53,23 @@ func handleProxyUser(c *gin.Context) *models.User { return user } -func proxyAuth() gin.HandlerFunc { +func (rt *Router) proxyAuth() gin.HandlerFunc { return func(c *gin.Context) { - user := handleProxyUser(c) + user := rt.handleProxyUser(c) c.Set("userid", user.Id) c.Set("username", user.Username) c.Next() } } -func jwtAuth() gin.HandlerFunc { +func (rt *Router) jwtAuth() gin.HandlerFunc { return func(c *gin.Context) { - metadata, err := extractTokenMetadata(c.Request) + metadata, err := rt.extractTokenMetadata(c.Request) if err != nil { ginx.Bomb(http.StatusUnauthorized, "unauthorized") } - userIdentity, err := fetchAuth(c.Request.Context(), metadata.AccessUuid) + userIdentity, err := rt.fetchAuth(c.Request.Context(), metadata.AccessUuid) if err != nil { ginx.Bomb(http.StatusUnauthorized, "unauthorized") } @@ -94,25 +92,25 @@ func jwtAuth() gin.HandlerFunc { } } -func auth() gin.HandlerFunc { - if config.C.ProxyAuth.Enable { - return proxyAuth() +func (rt *Router) auth() gin.HandlerFunc { + if rt.Center.ProxyAuth.Enable { + return rt.proxyAuth() } else { - return jwtAuth() + return rt.jwtAuth() } } // if proxy auth is enabled, mock jwt login/logout/refresh request -func jwtMock() gin.HandlerFunc { +func (rt *Router) jwtMock() gin.HandlerFunc { return func(c *gin.Context) { - if !config.C.ProxyAuth.Enable { + if !rt.Center.ProxyAuth.Enable { c.Next() return } if strings.Contains(c.FullPath(), "logout") { ginx.Bomb(http.StatusBadRequest, "logout is not supported when proxy auth is enabled") } - user := handleProxyUser(c) + user := rt.handleProxyUser(c) ginx.NewRender(c).Data(gin.H{ "user": user, "access_token": "", @@ -122,11 +120,11 @@ func jwtMock() gin.HandlerFunc { } } -func user() gin.HandlerFunc { +func (rt *Router) user() gin.HandlerFunc { return func(c *gin.Context) { userid := c.MustGet("userid").(int64) - user, err := models.UserGetById(userid) + user, err := models.UserGetById(rt.Ctx, userid) if err != nil { ginx.Bomb(http.StatusUnauthorized, "unauthorized") } @@ -141,12 +139,12 @@ func user() gin.HandlerFunc { } } -func userGroupWrite() gin.HandlerFunc { +func (rt *Router) userGroupWrite() gin.HandlerFunc { return func(c *gin.Context) { me := c.MustGet("user").(*models.User) - ug := UserGroup(ginx.UrlParamInt64(c, "id")) + ug := UserGroup(rt.Ctx, ginx.UrlParamInt64(c, "id")) - can, err := me.CanModifyUserGroup(ug) + can, err := me.CanModifyUserGroup(rt.Ctx, ug) ginx.Dangerous(err) if !can { @@ -158,12 +156,12 @@ func userGroupWrite() gin.HandlerFunc { } } -func bgro() gin.HandlerFunc { +func (rt *Router) bgro() gin.HandlerFunc { return func(c *gin.Context) { me := c.MustGet("user").(*models.User) - bg := BusiGroup(ginx.UrlParamInt64(c, "id")) + bg := BusiGroup(rt.Ctx, ginx.UrlParamInt64(c, "id")) - can, err := me.CanDoBusiGroup(bg) + can, err := me.CanDoBusiGroup(rt.Ctx, bg) ginx.Dangerous(err) if !can { @@ -176,12 +174,12 @@ func bgro() gin.HandlerFunc { } // bgrw 逐步要被干掉,不安全 -func bgrw() gin.HandlerFunc { +func (rt *Router) bgrw() gin.HandlerFunc { return func(c *gin.Context) { me := c.MustGet("user").(*models.User) - bg := BusiGroup(ginx.UrlParamInt64(c, "id")) + bg := BusiGroup(rt.Ctx, ginx.UrlParamInt64(c, "id")) - can, err := me.CanDoBusiGroup(bg, "rw") + can, err := me.CanDoBusiGroup(rt.Ctx, bg, "rw") ginx.Dangerous(err) if !can { @@ -194,11 +192,11 @@ func bgrw() gin.HandlerFunc { } // bgrwCheck 要逐渐替换掉bgrw方法,更安全 -func bgrwCheck(c *gin.Context, bgid int64) { +func (rt *Router) bgrwCheck(c *gin.Context, bgid int64) { me := c.MustGet("user").(*models.User) - bg := BusiGroup(bgid) + bg := BusiGroup(rt.Ctx, bgid) - can, err := me.CanDoBusiGroup(bg, "rw") + can, err := me.CanDoBusiGroup(rt.Ctx, bg, "rw") ginx.Dangerous(err) if !can { @@ -208,7 +206,7 @@ func bgrwCheck(c *gin.Context, bgid int64) { c.Set("busi_group", bg) } -func bgrwChecks(c *gin.Context, bgids []int64) { +func (rt *Router) bgrwChecks(c *gin.Context, bgids []int64) { set := make(map[int64]struct{}) for i := 0; i < len(bgids); i++ { @@ -216,16 +214,16 @@ func bgrwChecks(c *gin.Context, bgids []int64) { continue } - bgrwCheck(c, bgids[i]) + rt.bgrwCheck(c, bgids[i]) set[bgids[i]] = struct{}{} } } -func bgroCheck(c *gin.Context, bgid int64) { +func (rt *Router) bgroCheck(c *gin.Context, bgid int64) { me := c.MustGet("user").(*models.User) - bg := BusiGroup(bgid) + bg := BusiGroup(rt.Ctx, bgid) - can, err := me.CanDoBusiGroup(bg) + can, err := me.CanDoBusiGroup(rt.Ctx, bg) ginx.Dangerous(err) if !can { @@ -235,11 +233,11 @@ func bgroCheck(c *gin.Context, bgid int64) { c.Set("busi_group", bg) } -func perm(operation string) gin.HandlerFunc { +func (rt *Router) perm(operation string) gin.HandlerFunc { return func(c *gin.Context) { me := c.MustGet("user").(*models.User) - can, err := me.CheckPerm(operation) + can, err := me.CheckPerm(rt.Ctx, operation) ginx.Dangerous(err) if !can { @@ -250,11 +248,11 @@ func perm(operation string) gin.HandlerFunc { } } -func admin() gin.HandlerFunc { +func (rt *Router) admin() gin.HandlerFunc { return func(c *gin.Context) { userid := c.MustGet("userid").(int64) - user, err := models.UserGetById(userid) + user, err := models.UserGetById(rt.Ctx, userid) if err != nil { ginx.Bomb(http.StatusUnauthorized, "unauthorized") } @@ -281,8 +279,8 @@ func admin() gin.HandlerFunc { } } -func extractTokenMetadata(r *http.Request) (*AccessDetails, error) { - token, err := verifyToken(config.C.JWTAuth.SigningKey, extractToken(r)) +func (rt *Router) extractTokenMetadata(r *http.Request) (*AccessDetails, error) { + token, err := rt.verifyToken(rt.Center.JWTAuth.SigningKey, rt.extractToken(r)) if err != nil { return nil, err } @@ -303,7 +301,7 @@ func extractTokenMetadata(r *http.Request) (*AccessDetails, error) { return nil, err } -func extractToken(r *http.Request) string { +func (rt *Router) extractToken(r *http.Request) string { tok := r.Header.Get("Authorization") if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " { @@ -313,17 +311,17 @@ func extractToken(r *http.Request) string { return "" } -func createAuth(ctx context.Context, userIdentity string, td *TokenDetails) error { +func (rt *Router) createAuth(ctx context.Context, userIdentity string, td *TokenDetails) error { at := time.Unix(td.AtExpires, 0) - rt := time.Unix(td.RtExpires, 0) + rte := time.Unix(td.RtExpires, 0) now := time.Now() - errAccess := storage.Redis.Set(ctx, wrapJwtKey(td.AccessUuid), userIdentity, at.Sub(now)).Err() + errAccess := rt.Redis.Set(ctx, rt.wrapJwtKey(td.AccessUuid), userIdentity, at.Sub(now)).Err() if errAccess != nil { return errAccess } - errRefresh := storage.Redis.Set(ctx, wrapJwtKey(td.RefreshUuid), userIdentity, rt.Sub(now)).Err() + errRefresh := rt.Redis.Set(ctx, rt.wrapJwtKey(td.RefreshUuid), userIdentity, rte.Sub(now)).Err() if errRefresh != nil { return errRefresh } @@ -331,26 +329,26 @@ func createAuth(ctx context.Context, userIdentity string, td *TokenDetails) erro return nil } -func fetchAuth(ctx context.Context, givenUuid string) (string, error) { - return storage.Redis.Get(ctx, wrapJwtKey(givenUuid)).Result() +func (rt *Router) fetchAuth(ctx context.Context, givenUuid string) (string, error) { + return rt.Redis.Get(ctx, rt.wrapJwtKey(givenUuid)).Result() } -func deleteAuth(ctx context.Context, givenUuid string) error { - return storage.Redis.Del(ctx, wrapJwtKey(givenUuid)).Err() +func (rt *Router) deleteAuth(ctx context.Context, givenUuid string) error { + return rt.Redis.Del(ctx, rt.wrapJwtKey(givenUuid)).Err() } -func deleteTokens(ctx context.Context, authD *AccessDetails) error { +func (rt *Router) deleteTokens(ctx context.Context, authD *AccessDetails) error { // get the refresh uuid refreshUuid := authD.AccessUuid + "++" + authD.UserIdentity // delete access token - err := storage.Redis.Del(ctx, wrapJwtKey(authD.AccessUuid)).Err() + err := rt.Redis.Del(ctx, rt.wrapJwtKey(authD.AccessUuid)).Err() if err != nil { return err } // delete refresh token - err = storage.Redis.Del(ctx, wrapJwtKey(refreshUuid)).Err() + err = rt.Redis.Del(ctx, rt.wrapJwtKey(refreshUuid)).Err() if err != nil { return err } @@ -358,8 +356,8 @@ func deleteTokens(ctx context.Context, authD *AccessDetails) error { return nil } -func wrapJwtKey(key string) string { - return config.C.JWTAuth.RedisKeyPrefix + key +func (rt *Router) wrapJwtKey(key string) string { + return rt.Center.JWTAuth.RedisKeyPrefix + key } type TokenDetails struct { @@ -371,12 +369,12 @@ type TokenDetails struct { RtExpires int64 } -func createTokens(signingKey, userIdentity string) (*TokenDetails, error) { +func (rt *Router) createTokens(signingKey, userIdentity string) (*TokenDetails, error) { td := &TokenDetails{} - td.AtExpires = time.Now().Add(time.Minute * time.Duration(config.C.JWTAuth.AccessExpired)).Unix() + td.AtExpires = time.Now().Add(time.Minute * time.Duration(rt.Center.JWTAuth.AccessExpired)).Unix() td.AccessUuid = uuid.NewString() - td.RtExpires = time.Now().Add(time.Minute * time.Duration(config.C.JWTAuth.RefreshExpired)).Unix() + td.RtExpires = time.Now().Add(time.Minute * time.Duration(rt.Center.JWTAuth.RefreshExpired)).Unix() td.RefreshUuid = td.AccessUuid + "++" + userIdentity var err error @@ -397,8 +395,8 @@ func createTokens(signingKey, userIdentity string) (*TokenDetails, error) { rtClaims["refresh_uuid"] = td.RefreshUuid rtClaims["user_identity"] = userIdentity rtClaims["exp"] = td.RtExpires - rt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtClaims) - td.RefreshToken, err = rt.SignedString([]byte(signingKey)) + jrt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtClaims) + td.RefreshToken, err = jrt.SignedString([]byte(signingKey)) if err != nil { return nil, err } @@ -406,7 +404,7 @@ func createTokens(signingKey, userIdentity string) (*TokenDetails, error) { return td, nil } -func verifyToken(signingKey, tokenString string) (*jwt.Token, error) { +func (rt *Router) verifyToken(signingKey, tokenString string) (*jwt.Token, error) { if tokenString == "" { return nil, fmt.Errorf("bearer token not found") } diff --git a/center/router/router_notify_config.go b/center/router/router_notify_config.go new file mode 100644 index 0000000000000000000000000000000000000000..595a77a61527720f01aead551814d7e0b9cf7b07 --- /dev/null +++ b/center/router/router_notify_config.go @@ -0,0 +1,135 @@ +package router + +import ( + "encoding/json" + + "github.com/ccfos/nightingale/v6/models" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) webhookGets(c *gin.Context) { + var webhooks []models.Webhook + cval, err := models.ConfigsGet(rt.Ctx, models.WEBHOOKKEY) + ginx.Dangerous(err) + if cval == "" { + ginx.NewRender(c).Data(webhooks, nil) + return + } + + err = json.Unmarshal([]byte(cval), &webhooks) + ginx.NewRender(c).Data(webhooks, err) +} + +func (rt *Router) webhookPuts(c *gin.Context) { + var webhooks []models.Webhook + ginx.BindJSON(c, &webhooks) + for i := 0; i < len(webhooks); i++ { + for k, v := range webhooks[i].HeaderMap { + webhooks[i].Headers = append(webhooks[i].Headers, k) + webhooks[i].Headers = append(webhooks[i].Headers, v) + } + } + + data, err := json.Marshal(webhooks) + ginx.Dangerous(err) + + ginx.NewRender(c).Message(models.ConfigsSet(rt.Ctx, models.WEBHOOKKEY, string(data))) +} + +func (rt *Router) notifyScriptGet(c *gin.Context) { + var notifyScript models.NotifyScript + cval, err := models.ConfigsGet(rt.Ctx, models.NOTIFYSCRIPT) + ginx.Dangerous(err) + + if cval == "" { + ginx.NewRender(c).Data(notifyScript, nil) + return + } + + err = json.Unmarshal([]byte(cval), ¬ifyScript) + ginx.NewRender(c).Data(notifyScript, err) +} + +func (rt *Router) notifyScriptPut(c *gin.Context) { + var notifyScript models.NotifyScript + ginx.BindJSON(c, ¬ifyScript) + + data, err := json.Marshal(notifyScript) + ginx.Dangerous(err) + + ginx.NewRender(c).Message(models.ConfigsSet(rt.Ctx, models.NOTIFYSCRIPT, string(data))) +} + +func (rt *Router) notifyChannelGets(c *gin.Context) { + var notifyChannels []models.NotifyChannel + cval, err := models.ConfigsGet(rt.Ctx, models.NOTIFYCHANNEL) + ginx.Dangerous(err) + if cval == "" { + ginx.NewRender(c).Data(notifyChannels, nil) + return + } + + err = json.Unmarshal([]byte(cval), ¬ifyChannels) + ginx.NewRender(c).Data(notifyChannels, err) +} + +func (rt *Router) notifyChannelPuts(c *gin.Context) { + var notifyChannels []models.NotifyChannel + ginx.BindJSON(c, ¬ifyChannels) + + channels := []string{models.Dingtalk, models.Wecom, models.Feishu, models.Mm, models.Telegram, models.Email} + + m := make(map[string]struct{}) + for _, v := range notifyChannels { + m[v.Ident] = struct{}{} + } + + for _, v := range channels { + if _, ok := m[v]; !ok { + ginx.Bomb(200, "channel %s ident can not modify", v) + } + } + + data, err := json.Marshal(notifyChannels) + ginx.Dangerous(err) + + ginx.NewRender(c).Message(models.ConfigsSet(rt.Ctx, models.NOTIFYCHANNEL, string(data))) +} + +func (rt *Router) notifyContactGets(c *gin.Context) { + var notifyContacts []models.NotifyContact + cval, err := models.ConfigsGet(rt.Ctx, models.NOTIFYCONTACT) + ginx.Dangerous(err) + if cval == "" { + ginx.NewRender(c).Data(notifyContacts, nil) + return + } + + err = json.Unmarshal([]byte(cval), ¬ifyContacts) + ginx.NewRender(c).Data(notifyContacts, err) +} + +func (rt *Router) notifyContactPuts(c *gin.Context) { + var notifyContacts []models.NotifyContact + ginx.BindJSON(c, ¬ifyContacts) + + keys := []string{models.DingtalkKey, models.WecomKey, models.FeishuKey, models.MmKey, models.TelegramKey} + + m := make(map[string]struct{}) + for _, v := range notifyContacts { + m[v.Ident] = struct{}{} + } + + for _, v := range keys { + if _, ok := m[v]; !ok { + ginx.Bomb(200, "contact %s ident can not modify", v) + } + } + + data, err := json.Marshal(notifyContacts) + ginx.Dangerous(err) + + ginx.NewRender(c).Message(models.ConfigsSet(rt.Ctx, models.NOTIFYCONTACT, string(data))) +} diff --git a/center/router/router_notify_tpl.go b/center/router/router_notify_tpl.go new file mode 100644 index 0000000000000000000000000000000000000000..09c3f5421abd5e53ba45d40ecabee6267f777c9a --- /dev/null +++ b/center/router/router_notify_tpl.go @@ -0,0 +1,63 @@ +package router + +import ( + "bytes" + "encoding/json" + "html/template" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/tplx" + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) notifyTplGets(c *gin.Context) { + lst, err := models.NotifyTplGets(rt.Ctx) + + ginx.NewRender(c).Data(lst, err) +} + +func (rt *Router) notifyTplUpdateContent(c *gin.Context) { + var f models.NotifyTpl + ginx.BindJSON(c, &f) + + ginx.NewRender(c).Message(f.UpdateContent(rt.Ctx)) +} + +func (rt *Router) notifyTplUpdate(c *gin.Context) { + var f models.NotifyTpl + ginx.BindJSON(c, &f) + + ginx.NewRender(c).Message(f.Update(rt.Ctx)) +} + +func (rt *Router) notifyTplPreview(c *gin.Context) { + b, err := file.ToBytes("./etc/event.json") + if err != nil { + ginx.NewRender(c).Message(err.Error()) + return + } + var event models.AlertCurEvent + err = json.Unmarshal(b, &event) + if err != nil { + ginx.NewRender(c).Message(err.Error()) + return + } + + var f models.NotifyTpl + ginx.BindJSON(c, &f) + + tpl, err := template.New(f.Channel).Funcs(tplx.TemplateFuncMap).Parse(f.Content) + ginx.Dangerous(err) + + var body bytes.Buffer + var ret string + if err := tpl.Execute(&body, event); err != nil { + ret = err.Error() + } else { + ret = body.String() + } + + ginx.NewRender(c).Data(ret, nil) +} diff --git a/center/router/router_proxy.go b/center/router/router_proxy.go new file mode 100644 index 0000000000000000000000000000000000000000..3f7fc240a5423c91a8e5667fd9f9edda31fbd591 --- /dev/null +++ b/center/router/router_proxy.go @@ -0,0 +1,129 @@ +package router + +import ( + "context" + "net" + "net/http" + "net/http/httputil" + "net/url" + "strings" + "time" + + pkgprom "github.com/ccfos/nightingale/v6/pkg/prom" + "github.com/gin-gonic/gin" + "github.com/prometheus/common/model" + "github.com/toolkits/pkg/ginx" +) + +type queryFormItem struct { + Start int64 `json:"start" binding:"required"` + End int64 `json:"end" binding:"required"` + Step int64 `json:"step" binding:"required"` + Query string `json:"query" binding:"required"` +} + +type batchQueryForm struct { + DatasourceId int64 `json:"datasource_id" binding:"required"` + Queries []queryFormItem `json:"queries" binding:"required"` +} + +func (rt *Router) promBatchQueryRange(c *gin.Context) { + var f batchQueryForm + ginx.Dangerous(c.BindJSON(&f)) + + cli := rt.PromClients.GetCli(f.DatasourceId) + + var lst []model.Value + + for _, item := range f.Queries { + r := pkgprom.Range{ + Start: time.Unix(item.Start, 0), + End: time.Unix(item.End, 0), + Step: time.Duration(item.Step) * time.Second, + } + + resp, _, err := cli.QueryRange(context.Background(), item.Query, r) + ginx.Dangerous(err) + + lst = append(lst, resp) + } + + ginx.NewRender(c).Data(lst, nil) +} + +func (rt *Router) dsProxy(c *gin.Context) { + dsId := ginx.UrlParamInt64(c, "id") + ds := rt.DatasourceCache.GetBId(dsId) + + if ds == nil { + c.String(http.StatusBadRequest, "no such datasource") + return + } + + target, err := url.Parse(ds.HTTPJson.Url) + if err != nil { + c.String(http.StatusInternalServerError, "invalid url: %s", ds.HTTPJson.Url) + return + } + + director := func(req *http.Request) { + req.URL.Scheme = target.Scheme + req.URL.Host = target.Host + req.Host = target.Host + + req.Header.Set("Host", target.Host) + + // fe request e.g. /api/n9e/proxy/:id/* + arr := strings.Split(req.URL.Path, "/") + if len(arr) < 6 { + c.String(http.StatusBadRequest, "invalid url path") + return + } + + req.URL.Path = strings.TrimRight(target.Path, "/") + "/" + strings.Join(arr[5:], "/") + if target.RawQuery == "" || req.URL.RawQuery == "" { + req.URL.RawQuery = target.RawQuery + req.URL.RawQuery + } else { + req.URL.RawQuery = target.RawQuery + "&" + req.URL.RawQuery + } + + if _, ok := req.Header["User-Agent"]; !ok { + req.Header.Set("User-Agent", "") + } + + if ds.AuthJson.BasicAuthUser != "" { + req.SetBasicAuth(ds.AuthJson.BasicAuthUser, ds.AuthJson.BasicAuthPassword) + } + + headerCount := len(ds.HTTPJson.Headers) + if headerCount > 0 { + for key, value := range ds.HTTPJson.Headers { + req.Header.Set(key, value) + if key == "Host" { + req.Host = value + } + } + } + } + + errFunc := func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadGateway) + } + + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: time.Duration(ds.HTTPJson.DialTimeout) * time.Millisecond, + }).DialContext, + ResponseHeaderTimeout: time.Duration(ds.HTTPJson.Timeout) * time.Millisecond, + MaxIdleConnsPerHost: ds.HTTPJson.MaxIdleConnsPerHost, + } + + proxy := &httputil.ReverseProxy{ + Director: director, + Transport: transport, + ErrorHandler: errFunc, + } + + proxy.ServeHTTP(c.Writer, c.Request) +} diff --git a/src/webapi/router/router_recording_rule.go b/center/router/router_recording_rule.go similarity index 66% rename from src/webapi/router/router_recording_rule.go rename to center/router/router_recording_rule.go index 5c8729af102fc6e326ea7f4d65bc237b2ec2fad5..66ce3950b805f2dc581354d16f9794780c887f3e 100644 --- a/src/webapi/router/router_recording_rule.go +++ b/center/router/router_recording_rule.go @@ -4,21 +4,22 @@ import ( "net/http" "time" - "github.com/didi/nightingale/v5/src/models" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" ) -func recordingRuleGets(c *gin.Context) { +func (rt *Router) recordingRuleGets(c *gin.Context) { busiGroupId := ginx.UrlParamInt64(c, "id") - ars, err := models.RecordingRuleGets(busiGroupId) + ars, err := models.RecordingRuleGets(rt.Ctx, busiGroupId) ginx.NewRender(c).Data(ars, err) } -func recordingRuleGet(c *gin.Context) { +func (rt *Router) recordingRuleGet(c *gin.Context) { rrid := ginx.UrlParamInt64(c, "rrid") - ar, err := models.RecordingRuleGetById(rrid) + ar, err := models.RecordingRuleGetById(rt.Ctx, rrid) ginx.Dangerous(err) if ar == nil { @@ -29,7 +30,7 @@ func recordingRuleGet(c *gin.Context) { ginx.NewRender(c).Data(ar, err) } -func recordingRuleAddByFE(c *gin.Context) { +func (rt *Router) recordingRuleAddByFE(c *gin.Context) { username := c.MustGet("username").(string) var lst []models.RecordingRule @@ -49,7 +50,7 @@ func recordingRuleAddByFE(c *gin.Context) { lst[i].UpdateBy = username lst[i].FE2DB() - if err := lst[i].Add(); err != nil { + if err := lst[i].Add(rt.Ctx); err != nil { reterr[lst[i].Name] = err.Error() } else { reterr[lst[i].Name] = "" @@ -58,12 +59,12 @@ func recordingRuleAddByFE(c *gin.Context) { ginx.NewRender(c).Data(reterr, nil) } -func recordingRulePutByFE(c *gin.Context) { +func (rt *Router) recordingRulePutByFE(c *gin.Context) { var f models.RecordingRule ginx.BindJSON(c, &f) rrid := ginx.UrlParamInt64(c, "rrid") - ar, err := models.RecordingRuleGetById(rrid) + ar, err := models.RecordingRuleGetById(rt.Ctx, rrid) ginx.Dangerous(err) if ar == nil { @@ -71,19 +72,19 @@ func recordingRulePutByFE(c *gin.Context) { return } - bgrwCheck(c, ar.GroupId) + rt.bgrwCheck(c, ar.GroupId) f.UpdateBy = c.MustGet("username").(string) - ginx.NewRender(c).Message(ar.Update(f)) + ginx.NewRender(c).Message(ar.Update(rt.Ctx, f)) } -func recordingRuleDel(c *gin.Context) { +func (rt *Router) recordingRuleDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() - ginx.NewRender(c).Message(models.RecordingRuleDels(f.Ids, ginx.UrlParamInt64(c, "id"))) + ginx.NewRender(c).Message(models.RecordingRuleDels(rt.Ctx, f.Ids, ginx.UrlParamInt64(c, "id"))) } @@ -92,7 +93,7 @@ type recordRuleFieldForm struct { Fields map[string]interface{} `json:"fields"` } -func recordingRulePutFields(c *gin.Context) { +func (rt *Router) recordingRulePutFields(c *gin.Context) { var f recordRuleFieldForm ginx.BindJSON(c, &f) @@ -104,14 +105,14 @@ func recordingRulePutFields(c *gin.Context) { f.Fields["update_at"] = time.Now().Unix() for i := 0; i < len(f.Ids); i++ { - ar, err := models.RecordingRuleGetById(f.Ids[i]) + ar, err := models.RecordingRuleGetById(rt.Ctx, f.Ids[i]) ginx.Dangerous(err) if ar == nil { continue } - ginx.Dangerous(ar.UpdateFieldsMap(f.Fields)) + ginx.Dangerous(ar.UpdateFieldsMap(rt.Ctx, f.Fields)) } ginx.NewRender(c).Message(nil) diff --git a/center/router/router_role.go b/center/router/router_role.go new file mode 100644 index 0000000000000000000000000000000000000000..75eaa2ef862b019f2e5fee5317350220540ad4d3 --- /dev/null +++ b/center/router/router_role.go @@ -0,0 +1,85 @@ +package router + +import ( + "net/http" + "strings" + + "github.com/ccfos/nightingale/v6/models" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) rolesGets(c *gin.Context) { + lst, err := models.RoleGetsAll(rt.Ctx) + ginx.NewRender(c).Data(lst, err) +} + +func (rt *Router) permsGets(c *gin.Context) { + user := c.MustGet("user").(*models.User) + lst, err := models.OperationsOfRole(rt.Ctx, strings.Fields(user.Roles)) + ginx.NewRender(c).Data(lst, err) +} + +// 创建角色 +func (rt *Router) roleAdd(c *gin.Context) { + var f models.Role + ginx.BindJSON(c, &f) + + err := f.Add(rt.Ctx) + ginx.NewRender(c).Message(err) +} + +// 更新角色 +func (rt *Router) rolePut(c *gin.Context) { + var f models.Role + ginx.BindJSON(c, &f) + oldRule, err := models.RoleGet(rt.Ctx, "id=?", f.Id) + ginx.Dangerous(err) + + if oldRule == nil { + ginx.Bomb(http.StatusOK, "role not found") + } + + if oldRule.Name == "Admin" { + ginx.Bomb(http.StatusOK, "admin role can not be modified") + } + + if oldRule.Name != f.Name { + // name changed, check duplication + num, err := models.RoleCount(rt.Ctx, "name=? and id<>?", f.Name, oldRule.Id) + ginx.Dangerous(err) + + if num > 0 { + ginx.Bomb(http.StatusOK, "role name already exists") + } + } + + oldRule.Name = f.Name + oldRule.Note = f.Note + + ginx.NewRender(c).Message(oldRule.Update(rt.Ctx, "name", "note")) +} + +func (rt *Router) roleDel(c *gin.Context) { + id := ginx.UrlParamInt64(c, "id") + target, err := models.RoleGet(rt.Ctx, "id=?", id) + ginx.Dangerous(err) + + if target.Name == "Admin" { + ginx.Bomb(http.StatusOK, "admin role can not be modified") + } + + if target == nil { + ginx.NewRender(c).Message(nil) + return + } + + ginx.NewRender(c).Message(target.Del(rt.Ctx)) +} + +// 角色列表 +func (rt *Router) roleGets(c *gin.Context) { + lst, err := models.RoleGetsAll(rt.Ctx) + ginx.NewRender(c).Data(lst, err) +} diff --git a/center/router/router_role_operation.go b/center/router/router_role_operation.go new file mode 100644 index 0000000000000000000000000000000000000000..e6d1c4c213c4d250980cbcabad684ccb9e21455f --- /dev/null +++ b/center/router/router_role_operation.go @@ -0,0 +1,43 @@ +package router + +import ( + "net/http" + + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) operationOfRole(c *gin.Context) { + id := ginx.UrlParamInt64(c, "id") + role, err := models.RoleGet(rt.Ctx, "id=?", id) + ginx.Dangerous(err) + if role == nil { + ginx.Bomb(http.StatusOK, "role not found") + } + + ops, err := models.OperationsOfRole(rt.Ctx, []string{role.Name}) + ginx.NewRender(c).Data(ops, err) +} + +func (rt *Router) roleBindOperation(c *gin.Context) { + id := ginx.UrlParamInt64(c, "id") + role, err := models.RoleGet(rt.Ctx, "id=?", id) + ginx.Dangerous(err) + if role == nil { + ginx.Bomb(http.StatusOK, "role not found") + } + + if role.Name == "Admin" { + ginx.Bomb(http.StatusOK, "admin role can not be modified") + } + + var ops []string + ginx.BindJSON(c, &ops) + + ginx.NewRender(c).Message(models.RoleOperationBind(rt.Ctx, role.Name, ops)) +} + +func (rt *Router) operations(c *gin.Context) { + ginx.NewRender(c).Data(rt.Operations.Ops, nil) +} diff --git a/src/webapi/router/router_self.go b/center/router/router_self.go similarity index 71% rename from src/webapi/router/router_self.go rename to center/router/router_self.go index 22fb9e1c6c08f95c72e8bcfc49f397b6219a3e7f..1eeeeeda7f0dbbd8f2ed8bd3b367a95a4a09cce5 100644 --- a/src/webapi/router/router_self.go +++ b/center/router/router_self.go @@ -1,14 +1,14 @@ package router import ( + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/ormx" ) -func selfProfileGet(c *gin.Context) { +func (rt *Router) selfProfileGet(c *gin.Context) { user := c.MustGet("user").(*models.User) if user.IsAdmin() { user.Admin = true @@ -24,7 +24,7 @@ type selfProfileForm struct { Contacts ormx.JSONObj `json:"contacts"` } -func selfProfilePut(c *gin.Context) { +func (rt *Router) selfProfilePut(c *gin.Context) { var f selfProfileForm ginx.BindJSON(c, &f) @@ -36,7 +36,7 @@ func selfProfilePut(c *gin.Context) { user.Contacts = f.Contacts user.UpdateBy = user.Username - ginx.NewRender(c).Message(user.UpdateAllFields()) + ginx.NewRender(c).Message(user.UpdateAllFields(rt.Ctx)) } type selfPasswordForm struct { @@ -44,9 +44,9 @@ type selfPasswordForm struct { NewPass string `json:"newpass" binding:"required"` } -func selfPasswordPut(c *gin.Context) { +func (rt *Router) selfPasswordPut(c *gin.Context) { var f selfPasswordForm ginx.BindJSON(c, &f) user := c.MustGet("user").(*models.User) - ginx.NewRender(c).Message(user.ChangePassword(f.OldPass, f.NewPass)) + ginx.NewRender(c).Message(user.ChangePassword(rt.Ctx, f.OldPass, f.NewPass)) } diff --git a/center/router/router_server.go b/center/router/router_server.go new file mode 100644 index 0000000000000000000000000000000000000000..21b27535ba9d22fe9a2da78188cff8d07cae0c9e --- /dev/null +++ b/center/router/router_server.go @@ -0,0 +1,18 @@ +package router + +import ( + "github.com/ccfos/nightingale/v6/models" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func (rt *Router) serversGet(c *gin.Context) { + list, err := models.AlertingEngineGets(rt.Ctx, "") + ginx.NewRender(c).Data(list, err) +} + +func (rt *Router) serverClustersGet(c *gin.Context) { + list, err := models.AlertingEngineGetsClusters(rt.Ctx, "") + ginx.NewRender(c).Data(list, err) +} diff --git a/src/webapi/router/router_target.go b/center/router/router_target.go similarity index 61% rename from src/webapi/router/router_target.go rename to center/router/router_target.go index cde7d130b657e6fb81cff9df196534e3fdcaf9cb..55c8d160f6147c9132f06d065a9ad194c4e689d7 100644 --- a/src/webapi/router/router_target.go +++ b/center/router/router_target.go @@ -4,56 +4,79 @@ import ( "context" "fmt" "net/http" + "strconv" "strings" "time" + "github.com/ccfos/nightingale/v6/alert/common" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/prom" + "github.com/gin-gonic/gin" "github.com/prometheus/common/model" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/common/conv" - "github.com/didi/nightingale/v5/src/webapi/config" - "github.com/didi/nightingale/v5/src/webapi/prom" ) -func targetGets(c *gin.Context) { +type TargetQuery struct { + Filters []models.HostQuery `json:"queries"` + P int `json:"p"` + Limit int `json:"limit"` +} + +func (rt *Router) targetGetsByHostFilter(c *gin.Context) { + var f TargetQuery + ginx.BindJSON(c, &f) + + query := models.GetHostsQuery(f.Filters) + + hosts, err := models.TargetGetsByFilter(rt.Ctx, query, "", 0, f.Limit, (f.P-1)*f.Limit) + ginx.Dangerous(err) + + total, err := models.TargetCountByFilter(rt.Ctx, query, "", 0) + ginx.Dangerous(err) + + ginx.NewRender(c).Data(gin.H{ + "list": hosts, + "total": total, + }, nil) +} + +func (rt *Router) targetGets(c *gin.Context) { bgid := ginx.QueryInt64(c, "bgid", -1) query := ginx.QueryStr(c, "query", "") limit := ginx.QueryInt(c, "limit", 30) mins := ginx.QueryInt(c, "mins", 2) - clusters := queryClusters(c) + dsIds := queryDatasourceIds(c) - total, err := models.TargetTotal(bgid, clusters, query) + total, err := models.TargetTotal(rt.Ctx, bgid, dsIds, query) ginx.Dangerous(err) - list, err := models.TargetGets(bgid, clusters, query, limit, ginx.Offset(c, limit)) + list, err := models.TargetGets(rt.Ctx, bgid, dsIds, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) if err == nil { + now := time.Now() cache := make(map[int64]*models.BusiGroup) targetsMap := make(map[string]*models.Target) for i := 0; i < len(list); i++ { - ginx.Dangerous(list[i].FillGroup(cache)) - targetsMap[list[i].Cluster+list[i].Ident] = list[i] + ginx.Dangerous(list[i].FillGroup(rt.Ctx, cache)) + targetsMap[strconv.FormatInt(list[i].DatasourceId, 10)+list[i].Ident] = list[i] + if now.Unix()-list[i].UpdateAt < 60 { + list[i].TargetUp = 1 + } } - now := time.Now() - // query LoadPerCore / MemUtil / TargetUp / DiskUsedPercent from prometheus // map key: cluster, map value: ident list - targets := make(map[string][]string) + targets := make(map[int64][]string) for i := 0; i < len(list); i++ { - targets[list[i].Cluster] = append(targets[list[i].Cluster], list[i].Ident) + targets[list[i].DatasourceId] = append(targets[list[i].DatasourceId], list[i].Ident) } - for cluster := range targets { - cc, has := prom.Clusters.Get(cluster) - if !has { - continue - } + for dsId := range targets { + cc := rt.PromClients.GetCli(dsId) - targetArr := targets[cluster] + targetArr := targets[dsId] if len(targetArr) == 0 { continue } @@ -61,7 +84,7 @@ func targetGets(c *gin.Context) { targetRe := strings.Join(targetArr, "|") valuesMap := make(map[string]map[string]float64) - for metric, ql := range config.C.TargetMetrics { + for metric, ql := range rt.Center.TargetMetrics { promql := fmt.Sprintf(ql, targetRe, mins) values, err := instantQuery(context.Background(), cc, promql, now) ginx.Dangerous(err) @@ -71,15 +94,13 @@ func targetGets(c *gin.Context) { // handle values for metric, values := range valuesMap { for ident := range values { - mapkey := cluster + ident + mapkey := strconv.FormatInt(dsId, 10) + ident if t, has := targetsMap[mapkey]; has { switch metric { case "LoadPerCore": t.LoadPerCore = values[ident] case "MemUtil": t.MemUtil = values[ident] - case "TargetUp": - t.TargetUp = values[ident] case "DiskUtil": t.DiskUtil = values[ident] } @@ -95,10 +116,10 @@ func targetGets(c *gin.Context) { }, nil) } -func instantQuery(ctx context.Context, c *prom.ClusterType, promql string, ts time.Time) (map[string]float64, error) { +func instantQuery(ctx context.Context, c prom.API, promql string, ts time.Time) (map[string]float64, error) { ret := make(map[string]float64) - val, warnings, err := c.PromClient.Query(ctx, promql, ts) + val, warnings, err := c.Query(ctx, promql, ts) if err != nil { return ret, err } @@ -107,7 +128,8 @@ func instantQuery(ctx context.Context, c *prom.ClusterType, promql string, ts ti return ret, fmt.Errorf("instant query occur warnings, promql: %s, warnings: %v", promql, warnings) } - vectors := conv.ConvertVectors(val) + // TODO 替换函数 + vectors := common.ConvertAnomalyPoints(val) for i := range vectors { ident, has := vectors[i].Labels["ident"] if has { @@ -118,10 +140,10 @@ func instantQuery(ctx context.Context, c *prom.ClusterType, promql string, ts ti return ret, nil } -func targetGetTags(c *gin.Context) { - idents := ginx.QueryStr(c, "idents") +func (rt *Router) targetGetTags(c *gin.Context) { + idents := ginx.QueryStr(c, "idents", "") idents = strings.ReplaceAll(idents, ",", " ") - lst, err := models.TargetGetTags(strings.Fields(idents)) + lst, err := models.TargetGetTags(rt.Ctx, strings.Fields(idents)) ginx.NewRender(c).Data(lst, err) } @@ -134,7 +156,7 @@ func (t targetTagsForm) Verify() { } -func targetBindTagsByFE(c *gin.Context) { +func (rt *Router) targetBindTagsByFE(c *gin.Context) { var f targetTagsForm ginx.BindJSON(c, &f) @@ -142,12 +164,12 @@ func targetBindTagsByFE(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - checkTargetPerm(c, f.Idents) + rt.checkTargetPerm(c, f.Idents) - ginx.NewRender(c).Message(targetBindTags(f)) + ginx.NewRender(c).Message(rt.targetBindTags(f)) } -func targetBindTagsByService(c *gin.Context) { +func (rt *Router) targetBindTagsByService(c *gin.Context) { var f targetTagsForm ginx.BindJSON(c, &f) @@ -155,10 +177,10 @@ func targetBindTagsByService(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - ginx.NewRender(c).Message(targetBindTags(f)) + ginx.NewRender(c).Message(rt.targetBindTags(f)) } -func targetBindTags(f targetTagsForm) error { +func (rt *Router) targetBindTags(f targetTagsForm) error { for i := 0; i < len(f.Tags); i++ { arr := strings.Split(f.Tags[i], "=") if len(arr) != 2 { @@ -183,7 +205,7 @@ func targetBindTags(f targetTagsForm) error { } for i := 0; i < len(f.Idents); i++ { - target, err := models.TargetGetByIdent(f.Idents[i]) + target, err := models.TargetGetByIdent(rt.Ctx, f.Idents[i]) if err != nil { return err } @@ -201,7 +223,7 @@ func targetBindTags(f targetTagsForm) error { } } - err = target.AddTags(f.Tags) + err = target.AddTags(rt.Ctx, f.Tags) if err != nil { return err } @@ -209,7 +231,7 @@ func targetBindTags(f targetTagsForm) error { return nil } -func targetUnbindTagsByFE(c *gin.Context) { +func (rt *Router) targetUnbindTagsByFE(c *gin.Context) { var f targetTagsForm ginx.BindJSON(c, &f) @@ -217,12 +239,12 @@ func targetUnbindTagsByFE(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - checkTargetPerm(c, f.Idents) + rt.checkTargetPerm(c, f.Idents) - ginx.NewRender(c).Message(targetUnbindTags(f)) + ginx.NewRender(c).Message(rt.targetUnbindTags(f)) } -func targetUnbindTagsByService(c *gin.Context) { +func (rt *Router) targetUnbindTagsByService(c *gin.Context) { var f targetTagsForm ginx.BindJSON(c, &f) @@ -230,12 +252,12 @@ func targetUnbindTagsByService(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - ginx.NewRender(c).Message(targetUnbindTags(f)) + ginx.NewRender(c).Message(rt.targetUnbindTags(f)) } -func targetUnbindTags(f targetTagsForm) error { +func (rt *Router) targetUnbindTags(f targetTagsForm) error { for i := 0; i < len(f.Idents); i++ { - target, err := models.TargetGetByIdent(f.Idents[i]) + target, err := models.TargetGetByIdent(rt.Ctx, f.Idents[i]) if err != nil { return err } @@ -244,7 +266,7 @@ func targetUnbindTags(f targetTagsForm) error { continue } - err = target.DelTags(f.Tags) + err = target.DelTags(rt.Ctx, f.Tags) if err != nil { return err } @@ -257,7 +279,7 @@ type targetNoteForm struct { Note string `json:"note"` } -func targetUpdateNote(c *gin.Context) { +func (rt *Router) targetUpdateNote(c *gin.Context) { var f targetNoteForm ginx.BindJSON(c, &f) @@ -265,12 +287,12 @@ func targetUpdateNote(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - checkTargetPerm(c, f.Idents) + rt.checkTargetPerm(c, f.Idents) - ginx.NewRender(c).Message(models.TargetUpdateNote(f.Idents, f.Note)) + ginx.NewRender(c).Message(models.TargetUpdateNote(rt.Ctx, f.Idents, f.Note)) } -func targetUpdateNoteByService(c *gin.Context) { +func (rt *Router) targetUpdateNoteByService(c *gin.Context) { var f targetNoteForm ginx.BindJSON(c, &f) @@ -278,7 +300,7 @@ func targetUpdateNoteByService(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - ginx.NewRender(c).Message(models.TargetUpdateNote(f.Idents, f.Note)) + ginx.NewRender(c).Message(models.TargetUpdateNote(rt.Ctx, f.Idents, f.Note)) } type targetBgidForm struct { @@ -286,7 +308,7 @@ type targetBgidForm struct { Bgid int64 `json:"bgid"` } -func targetUpdateBgid(c *gin.Context) { +func (rt *Router) targetUpdateBgid(c *gin.Context) { var f targetBgidForm ginx.BindJSON(c, &f) @@ -296,7 +318,7 @@ func targetUpdateBgid(c *gin.Context) { user := c.MustGet("user").(*models.User) if user.IsAdmin() { - ginx.NewRender(c).Message(models.TargetUpdateBgid(f.Idents, f.Bgid, false)) + ginx.NewRender(c).Message(models.TargetUpdateBgid(rt.Ctx, f.Idents, f.Bgid, false)) return } @@ -304,7 +326,7 @@ func targetUpdateBgid(c *gin.Context) { // 把要操作的机器分成两部分,一部分是bgid为0,需要管理员分配,另一部分bgid>0,说明是业务组内部想调整 // 比如原来分配给didiyun的机器,didiyun的管理员想把部分机器调整到didiyun-ceph下 // 对于调整的这种情况,当前登录用户要对这批机器有操作权限,同时还要对目标BG有操作权限 - orphans, err := models.IdentsFilter(f.Idents, "group_id = ?", 0) + orphans, err := models.IdentsFilter(rt.Ctx, f.Idents, "group_id = ?", 0) ginx.Dangerous(err) // 机器里边存在未归组的,登录用户就需要是admin @@ -312,15 +334,15 @@ func targetUpdateBgid(c *gin.Context) { ginx.Bomb(http.StatusForbidden, "No permission. Only admin can assign BG") } - reBelongs, err := models.IdentsFilter(f.Idents, "group_id > ?", 0) + reBelongs, err := models.IdentsFilter(rt.Ctx, f.Idents, "group_id > ?", 0) ginx.Dangerous(err) if len(reBelongs) > 0 { // 对于这些要重新分配的机器,操作者要对这些机器本身有权限,同时要对目标bgid有权限 - checkTargetPerm(c, f.Idents) + rt.checkTargetPerm(c, f.Idents) - bg := BusiGroup(f.Bgid) - can, err := user.CanDoBusiGroup(bg, "rw") + bg := BusiGroup(rt.Ctx, f.Bgid) + can, err := user.CanDoBusiGroup(rt.Ctx, bg, "rw") ginx.Dangerous(err) if !can { @@ -329,19 +351,19 @@ func targetUpdateBgid(c *gin.Context) { } } else if f.Bgid == 0 { // 退还机器 - checkTargetPerm(c, f.Idents) + rt.checkTargetPerm(c, f.Idents) } else { ginx.Bomb(http.StatusBadRequest, "invalid bgid") } - ginx.NewRender(c).Message(models.TargetUpdateBgid(f.Idents, f.Bgid, false)) + ginx.NewRender(c).Message(models.TargetUpdateBgid(rt.Ctx, f.Idents, f.Bgid, false)) } type identsForm struct { Idents []string `json:"idents" binding:"required"` } -func targetDel(c *gin.Context) { +func (rt *Router) targetDel(c *gin.Context) { var f identsForm ginx.BindJSON(c, &f) @@ -349,14 +371,14 @@ func targetDel(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "idents empty") } - checkTargetPerm(c, f.Idents) + rt.checkTargetPerm(c, f.Idents) - ginx.NewRender(c).Message(models.TargetDel(f.Idents)) + ginx.NewRender(c).Message(models.TargetDel(rt.Ctx, f.Idents)) } -func checkTargetPerm(c *gin.Context, idents []string) { +func (rt *Router) checkTargetPerm(c *gin.Context, idents []string) { user := c.MustGet("user").(*models.User) - nopri, err := user.NopriIdents(idents) + nopri, err := user.NopriIdents(rt.Ctx, idents) ginx.Dangerous(err) if len(nopri) > 0 { diff --git a/src/webapi/router/router_task.go b/center/router/router_task.go similarity index 82% rename from src/webapi/router/router_task.go rename to center/router/router_task.go index 545c95202ca5f360bb5aecf05269ed4bd14821b9..b1ce98b599e7861b572623de0fc1ee52832838f6 100644 --- a/src/webapi/router/router_task.go +++ b/center/router/router_task.go @@ -8,15 +8,14 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/webapi/config" ) -func taskGets(c *gin.Context) { +func (rt *Router) taskGets(c *gin.Context) { bgid := ginx.UrlParamInt64(c, "id") mine := ginx.QueryBool(c, "mine", false) days := ginx.QueryInt64(c, "days", 7) @@ -31,10 +30,10 @@ func taskGets(c *gin.Context) { beginTime := time.Now().Unix() - days*24*3600 - total, err := models.TaskRecordTotal(bgid, beginTime, creator, query) + total, err := models.TaskRecordTotal(rt.Ctx, bgid, beginTime, creator, query) ginx.Dangerous(err) - list, err := models.TaskRecordGets(bgid, beginTime, creator, query, limit, ginx.Offset(c, limit)) + list, err := models.TaskRecordGets(rt.Ctx, bgid, beginTime, creator, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) ginx.NewRender(c).Data(gin.H{ @@ -121,7 +120,7 @@ func (f *taskForm) HandleFH(fh string) { f.Title = f.Title + " FH: " + fh } -func taskAdd(c *gin.Context) { +func (rt *Router) taskAdd(c *gin.Context) { var f taskForm ginx.BindJSON(c, &f) @@ -135,10 +134,10 @@ func taskAdd(c *gin.Context) { f.HandleFH(f.Hosts[0]) // check permission - checkTargetPerm(c, f.Hosts) + rt.checkTargetPerm(c, f.Hosts) // call ibex - taskId, err := TaskCreate(f) + taskId, err := TaskCreate(f, rt.Center.Ibex) ginx.Dangerous(err) if taskId <= 0 { @@ -149,9 +148,9 @@ func taskAdd(c *gin.Context) { record := models.TaskRecord{ Id: taskId, GroupId: bgid, - IbexAddress: config.C.Ibex.Address, - IbexAuthUser: config.C.Ibex.BasicAuthUser, - IbexAuthPass: config.C.Ibex.BasicAuthPass, + IbexAddress: rt.Center.Ibex.Address, + IbexAuthUser: rt.Center.Ibex.BasicAuthUser, + IbexAuthPass: rt.Center.Ibex.BasicAuthPass, Title: f.Title, Account: f.Account, Batch: f.Batch, @@ -164,14 +163,14 @@ func taskAdd(c *gin.Context) { CreateBy: f.Creator, } - err = record.Add() + err = record.Add(rt.Ctx) ginx.NewRender(c).Data(taskId, err) } -func taskProxy(c *gin.Context) { - target, err := url.Parse(config.C.Ibex.Address) +func (rt *Router) taskProxy(c *gin.Context) { + target, err := url.Parse(rt.Center.Ibex.Address) if err != nil { - ginx.NewRender(c).Message("invalid ibex address: %s", config.C.Ibex.Address) + ginx.NewRender(c).Message("invalid ibex address: %s", rt.Center.Ibex.Address) return } @@ -193,8 +192,8 @@ func taskProxy(c *gin.Context) { req.URL.RawQuery = target.RawQuery + "&" + req.URL.RawQuery } - if config.C.Ibex.BasicAuthUser != "" { - req.SetBasicAuth(config.C.Ibex.BasicAuthUser, config.C.Ibex.BasicAuthPass) + if rt.Center.Ibex.BasicAuthUser != "" { + req.SetBasicAuth(rt.Center.Ibex.BasicAuthUser, rt.Center.Ibex.BasicAuthPass) } } diff --git a/src/webapi/router/router_task_tpl.go b/center/router/router_task_tpl.go similarity index 75% rename from src/webapi/router/router_task_tpl.go rename to center/router/router_task_tpl.go index 60b9e306ac9f26053e0ab82486bd1aa361bc0c76..a4a36ee0720afe1954fa46bfa632913f040fe279 100644 --- a/src/webapi/router/router_task_tpl.go +++ b/center/router/router_task_tpl.go @@ -6,22 +6,22 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" ) -func taskTplGets(c *gin.Context) { +func (rt *Router) taskTplGets(c *gin.Context) { query := ginx.QueryStr(c, "query", "") limit := ginx.QueryInt(c, "limit", 20) groupId := ginx.UrlParamInt64(c, "id") - total, err := models.TaskTplTotal(groupId, query) + total, err := models.TaskTplTotal(rt.Ctx, groupId, query) ginx.Dangerous(err) - list, err := models.TaskTplGets(groupId, query, limit, ginx.Offset(c, limit)) + list, err := models.TaskTplGets(rt.Ctx, groupId, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) ginx.NewRender(c).Data(gin.H{ @@ -30,17 +30,17 @@ func taskTplGets(c *gin.Context) { }, nil) } -func taskTplGet(c *gin.Context) { +func (rt *Router) taskTplGet(c *gin.Context) { tid := ginx.UrlParamInt64(c, "tid") - tpl, err := models.TaskTplGet("id = ?", tid) + tpl, err := models.TaskTplGet(rt.Ctx, "id = ?", tid) ginx.Dangerous(err) if tpl == nil { ginx.Bomb(404, "no such task template") } - hosts, err := tpl.Hosts() + hosts, err := tpl.Hosts(rt.Ctx) ginx.NewRender(c).Data(gin.H{ "tpl": tpl, @@ -61,7 +61,7 @@ type taskTplForm struct { Hosts []string `json:"hosts"` } -func taskTplAdd(c *gin.Context) { +func (rt *Router) taskTplAdd(c *gin.Context) { var f taskTplForm ginx.BindJSON(c, &f) @@ -87,13 +87,13 @@ func taskTplAdd(c *gin.Context) { UpdateAt: now, } - ginx.NewRender(c).Message(tpl.Save(f.Hosts)) + ginx.NewRender(c).Message(tpl.Save(rt.Ctx, f.Hosts)) } -func taskTplPut(c *gin.Context) { +func (rt *Router) taskTplPut(c *gin.Context) { tid := ginx.UrlParamInt64(c, "tid") - tpl, err := models.TaskTplGet("id = ?", tid) + tpl, err := models.TaskTplGet(rt.Ctx, "id = ?", tid) ginx.Dangerous(err) if tpl == nil { @@ -120,13 +120,13 @@ func taskTplPut(c *gin.Context) { tpl.UpdateBy = user.Username tpl.UpdateAt = time.Now().Unix() - ginx.NewRender(c).Message(tpl.Update(f.Hosts)) + ginx.NewRender(c).Message(tpl.Update(rt.Ctx, f.Hosts)) } -func taskTplDel(c *gin.Context) { +func (rt *Router) taskTplDel(c *gin.Context) { tid := ginx.UrlParamInt64(c, "tid") - tpl, err := models.TaskTplGet("id = ?", tid) + tpl, err := models.TaskTplGet(rt.Ctx, "id = ?", tid) ginx.Dangerous(err) if tpl == nil { @@ -134,7 +134,7 @@ func taskTplDel(c *gin.Context) { return } - ginx.NewRender(c).Message(tpl.Del()) + ginx.NewRender(c).Message(tpl.Del(rt.Ctx)) } type tplTagsForm struct { @@ -171,7 +171,7 @@ func (f *tplTagsForm) Verify() { } } -func taskTplBindTags(c *gin.Context) { +func (rt *Router) taskTplBindTags(c *gin.Context) { var f tplTagsForm ginx.BindJSON(c, &f) f.Verify() @@ -179,20 +179,20 @@ func taskTplBindTags(c *gin.Context) { username := c.MustGet("username").(string) for i := 0; i < len(f.Ids); i++ { - tpl, err := models.TaskTplGet("id = ?", f.Ids[i]) + tpl, err := models.TaskTplGet(rt.Ctx, "id = ?", f.Ids[i]) ginx.Dangerous(err) if tpl == nil { continue } - ginx.Dangerous(tpl.AddTags(f.Tags, username)) + ginx.Dangerous(tpl.AddTags(rt.Ctx, f.Tags, username)) } ginx.NewRender(c).Message(nil) } -func taskTplUnbindTags(c *gin.Context) { +func (rt *Router) taskTplUnbindTags(c *gin.Context) { var f tplTagsForm ginx.BindJSON(c, &f) f.Verify() @@ -200,14 +200,14 @@ func taskTplUnbindTags(c *gin.Context) { username := c.MustGet("username").(string) for i := 0; i < len(f.Ids); i++ { - tpl, err := models.TaskTplGet("id = ?", f.Ids[i]) + tpl, err := models.TaskTplGet(rt.Ctx, "id = ?", f.Ids[i]) ginx.Dangerous(err) if tpl == nil { continue } - ginx.Dangerous(tpl.DelTags(f.Tags, username)) + ginx.Dangerous(tpl.DelTags(rt.Ctx, f.Tags, username)) } ginx.NewRender(c).Message(nil) diff --git a/src/webapi/router/router_user.go b/center/router/router_user.go similarity index 65% rename from src/webapi/router/router_user.go rename to center/router/router_user.go index 5b25d80513c1ab9d3aa27cc9c44f978f662e5217..3f4646a3f6ab61b13c93c2435d54986fa161f1a4 100644 --- a/src/webapi/router/router_user.go +++ b/center/router/router_user.go @@ -4,21 +4,21 @@ import ( "net/http" "strings" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/ormx" ) -func userFindAll(c *gin.Context) { +func (rt *Router) userFindAll(c *gin.Context) { limit := ginx.QueryInt(c, "limit", 20) query := ginx.QueryStr(c, "query", "") - total, err := models.UserTotal(query) + total, err := models.UserTotal(rt.Ctx, query) ginx.Dangerous(err) - list, err := models.UserGets(query, limit, ginx.Offset(c, limit)) + list, err := models.UserGets(rt.Ctx, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) ginx.NewRender(c).Data(gin.H{ @@ -27,14 +27,14 @@ func userFindAll(c *gin.Context) { }, nil) } -func userGets(c *gin.Context) { +func (rt *Router) userGets(c *gin.Context) { limit := ginx.QueryInt(c, "limit", 20) query := ginx.QueryStr(c, "query", "") - total, err := models.UserTotal(query) + total, err := models.UserTotal(rt.Ctx, query) ginx.Dangerous(err) - list, err := models.UserGets(query, limit, ginx.Offset(c, limit)) + list, err := models.UserGets(rt.Ctx, query, limit, ginx.Offset(c, limit)) ginx.Dangerous(err) user := c.MustGet("user").(*models.User) @@ -57,11 +57,11 @@ type userAddForm struct { Contacts ormx.JSONObj `json:"contacts"` } -func userAddPost(c *gin.Context) { +func (rt *Router) userAddPost(c *gin.Context) { var f userAddForm ginx.BindJSON(c, &f) - password, err := models.CryptoPass(f.Password) + password, err := models.CryptoPass(rt.Ctx, f.Password) ginx.Dangerous(err) if len(f.Roles) == 0 { @@ -83,11 +83,11 @@ func userAddPost(c *gin.Context) { UpdateBy: user.Username, } - ginx.NewRender(c).Message(u.Add()) + ginx.NewRender(c).Message(u.Add(rt.Ctx)) } -func userProfileGet(c *gin.Context) { - user := User(ginx.UrlParamInt64(c, "id")) +func (rt *Router) userProfileGet(c *gin.Context) { + user := User(rt.Ctx, ginx.UrlParamInt64(c, "id")) ginx.NewRender(c).Data(user, nil) } @@ -99,7 +99,7 @@ type userProfileForm struct { Contacts ormx.JSONObj `json:"contacts"` } -func userProfilePut(c *gin.Context) { +func (rt *Router) userProfilePut(c *gin.Context) { var f userProfileForm ginx.BindJSON(c, &f) @@ -107,7 +107,7 @@ func userProfilePut(c *gin.Context) { ginx.Bomb(http.StatusBadRequest, "roles empty") } - target := User(ginx.UrlParamInt64(c, "id")) + target := User(rt.Ctx, ginx.UrlParamInt64(c, "id")) target.Nickname = f.Nickname target.Phone = f.Phone target.Email = f.Email @@ -115,28 +115,28 @@ func userProfilePut(c *gin.Context) { target.Contacts = f.Contacts target.UpdateBy = c.MustGet("username").(string) - ginx.NewRender(c).Message(target.UpdateAllFields()) + ginx.NewRender(c).Message(target.UpdateAllFields(rt.Ctx)) } type userPasswordForm struct { Password string `json:"password" binding:"required"` } -func userPasswordPut(c *gin.Context) { +func (rt *Router) userPasswordPut(c *gin.Context) { var f userPasswordForm ginx.BindJSON(c, &f) - target := User(ginx.UrlParamInt64(c, "id")) + target := User(rt.Ctx, ginx.UrlParamInt64(c, "id")) - cryptoPass, err := models.CryptoPass(f.Password) + cryptoPass, err := models.CryptoPass(rt.Ctx, f.Password) ginx.Dangerous(err) - ginx.NewRender(c).Message(target.UpdatePassword(cryptoPass, c.MustGet("username").(string))) + ginx.NewRender(c).Message(target.UpdatePassword(rt.Ctx, cryptoPass, c.MustGet("username").(string))) } -func userDel(c *gin.Context) { +func (rt *Router) userDel(c *gin.Context) { id := ginx.UrlParamInt64(c, "id") - target, err := models.UserGetById(id) + target, err := models.UserGetById(rt.Ctx, id) ginx.Dangerous(err) if target == nil { @@ -144,5 +144,5 @@ func userDel(c *gin.Context) { return } - ginx.NewRender(c).Message(target.Del()) + ginx.NewRender(c).Message(target.Del(rt.Ctx)) } diff --git a/src/webapi/router/router_user_group.go b/center/router/router_user_group.go similarity index 59% rename from src/webapi/router/router_user_group.go rename to center/router/router_user_group.go index c5b653e4bde3ec612264014d080cb0e255f3006c..5d41e7a671d4b8070c7de317ea0ee8f3e26964a3 100644 --- a/src/webapi/router/router_user_group.go +++ b/center/router/router_user_group.go @@ -4,26 +4,27 @@ import ( "net/http" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/gin-gonic/gin" "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" + "github.com/toolkits/pkg/logger" ) -func checkBusiGroupPerm(c *gin.Context) { +func (rt *Router) checkBusiGroupPerm(c *gin.Context) { me := c.MustGet("user").(*models.User) - bg := BusiGroup(ginx.UrlParamInt64(c, "id")) + bg := BusiGroup(rt.Ctx, ginx.UrlParamInt64(c, "id")) - can, err := me.CanDoBusiGroup(bg, ginx.UrlParamStr(c, "perm")) + can, err := me.CanDoBusiGroup(rt.Ctx, bg, ginx.UrlParamStr(c, "perm")) ginx.NewRender(c).Data(can, err) } -func userGroupGets(c *gin.Context) { +func (rt *Router) userGroupGets(c *gin.Context) { limit := ginx.QueryInt(c, "limit", 1500) query := ginx.QueryStr(c, "query", "") me := c.MustGet("user").(*models.User) - lst, err := me.UserGroups(limit, query) + lst, err := me.UserGroups(rt.Ctx, limit, query) ginx.NewRender(c).Data(lst, err) } @@ -33,7 +34,7 @@ type userGroupForm struct { Note string `json:"note"` } -func userGroupAdd(c *gin.Context) { +func (rt *Router) userGroupAdd(c *gin.Context) { var f userGroupForm ginx.BindJSON(c, &f) @@ -46,16 +47,16 @@ func userGroupAdd(c *gin.Context) { UpdateBy: me.Username, } - err := ug.Add() + err := ug.Add(rt.Ctx) if err == nil { // Even failure is not a big deal - models.UserGroupMemberAdd(ug.Id, me.Id) + models.UserGroupMemberAdd(rt.Ctx, ug.Id, me.Id) } ginx.NewRender(c).Data(ug.Id, err) } -func userGroupPut(c *gin.Context) { +func (rt *Router) userGroupPut(c *gin.Context) { var f userGroupForm ginx.BindJSON(c, &f) @@ -64,7 +65,7 @@ func userGroupPut(c *gin.Context) { if ug.Name != f.Name { // name changed, check duplication - num, err := models.UserGroupCount("name=? and id<>?", f.Name, ug.Id) + num, err := models.UserGroupCount(rt.Ctx, "name=? and id<>?", f.Name, ug.Id) ginx.Dangerous(err) if num > 0 { @@ -77,17 +78,18 @@ func userGroupPut(c *gin.Context) { ug.UpdateBy = me.Username ug.UpdateAt = time.Now().Unix() - ginx.NewRender(c).Message(ug.Update("Name", "Note", "UpdateAt", "UpdateBy")) + ginx.NewRender(c).Message(ug.Update(rt.Ctx, "Name", "Note", "UpdateAt", "UpdateBy")) } // Return all members, front-end search and paging -func userGroupGet(c *gin.Context) { - ug := UserGroup(ginx.UrlParamInt64(c, "id")) +func (rt *Router) userGroupGet(c *gin.Context) { + ug := UserGroup(rt.Ctx, ginx.UrlParamInt64(c, "id")) - ids, err := models.MemberIds(ug.Id) + ids, err := models.MemberIds(rt.Ctx, ug.Id) ginx.Dangerous(err) - users, err := models.UserGetsByIds(ids) + logger.Info("userGroupGet", ids) + users, err := models.UserGetsByIds(rt.Ctx, ids) ginx.NewRender(c).Data(gin.H{ "users": users, @@ -95,12 +97,12 @@ func userGroupGet(c *gin.Context) { }, err) } -func userGroupDel(c *gin.Context) { +func (rt *Router) userGroupDel(c *gin.Context) { ug := c.MustGet("user_group").(*models.UserGroup) - ginx.NewRender(c).Message(ug.Del()) + ginx.NewRender(c).Message(ug.Del(rt.Ctx)) } -func userGroupMemberAdd(c *gin.Context) { +func (rt *Router) userGroupMemberAdd(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() @@ -108,17 +110,17 @@ func userGroupMemberAdd(c *gin.Context) { me := c.MustGet("user").(*models.User) ug := c.MustGet("user_group").(*models.UserGroup) - err := ug.AddMembers(f.Ids) + err := ug.AddMembers(rt.Ctx, f.Ids) if err == nil { ug.UpdateAt = time.Now().Unix() ug.UpdateBy = me.Username - ug.Update("UpdateAt", "UpdateBy") + ug.Update(rt.Ctx, "UpdateAt", "UpdateBy") } ginx.NewRender(c).Message(err) } -func userGroupMemberDel(c *gin.Context) { +func (rt *Router) userGroupMemberDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) f.Verify() @@ -126,11 +128,11 @@ func userGroupMemberDel(c *gin.Context) { me := c.MustGet("user").(*models.User) ug := c.MustGet("user_group").(*models.UserGroup) - err := ug.DelMembers(f.Ids) + err := ug.DelMembers(rt.Ctx, f.Ids) if err == nil { ug.UpdateAt = time.Now().Unix() ug.UpdateBy = me.Username - ug.Update("UpdateAt", "UpdateBy") + ug.Update(rt.Ctx, "UpdateAt", "UpdateBy") } ginx.NewRender(c).Message(err) diff --git a/center/sso/init.go b/center/sso/init.go new file mode 100644 index 0000000000000000000000000000000000000000..dcee321aa906c8519fc853336987322bb2fbe357 --- /dev/null +++ b/center/sso/init.go @@ -0,0 +1,76 @@ +package sso + +import ( + "github.com/ccfos/nightingale/v6/center/cconf" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/cas" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/ldapx" + "github.com/ccfos/nightingale/v6/pkg/oauth2x" + "github.com/ccfos/nightingale/v6/pkg/oidcx" + + "github.com/pelletier/go-toml/v2" + "github.com/toolkits/pkg/logger" +) + +type SsoClient struct { + OIDC *oidcx.SsoClient + LDAP *ldapx.SsoClient + CAS *cas.SsoClient + OAuth2 *oauth2x.SsoClient +} + +func Init(center cconf.Center, ctx *ctx.Context) *SsoClient { + ssoClient := new(SsoClient) + m := make(map[string]interface{}) + m["LDAP"] = center.LDAP + m["CAS"] = center.CAS + m["OIDC"] = center.OIDC + m["OAuth2"] = center.OAuth + + for name, config := range m { + b, err := toml.Marshal(config) + if err != nil { + logger.Error(err) + continue + } + + count, err := models.SsoConfigCountByName(ctx, name) + if err != nil { + logger.Error(err) + continue + } + + if count > 0 { + continue + } + + ssoConfig := models.SsoConfig{ + Name: name, + Content: string(b), + } + + err = ssoConfig.Create(ctx) + if err != nil { + logger.Error(err) + } + } + + // init ldap + ssoClient.LDAP = ldapx.New(center.LDAP) + + // init oidc + oidcClient, err := oidcx.New(center.OIDC) + if err != nil { + logger.Error("init oidc failed: %v", err) + } else { + ssoClient.OIDC = oidcClient + } + + // init cas + ssoClient.CAS = cas.New(center.CAS) + + // init oauth + ssoClient.OAuth2 = oauth2x.New(center.OAuth) + return ssoClient +} diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000000000000000000000000000000000000..5ea14f176ed7e50a523dea266aa91a4b32bb2fe2 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,9 @@ +package cli + +import ( + "github.com/ccfos/nightingale/v6/cli/upgrade" +) + +func Upgrade(configFile string, sqlFile string) error { + return upgrade.Upgrade(configFile, sqlFile) +} diff --git a/cli/upgrade/config.go b/cli/upgrade/config.go new file mode 100644 index 0000000000000000000000000000000000000000..235bc055f05766babbbde0c14cff5652318f4df8 --- /dev/null +++ b/cli/upgrade/config.go @@ -0,0 +1,63 @@ +package upgrade + +import ( + "bytes" + "path" + + "github.com/ccfos/nightingale/v6/pkg/cfg" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/ccfos/nightingale/v6/pkg/tlsx" + "github.com/koding/multiconfig" +) + +type Config struct { + DB ormx.DBConfig + Clusters []ClusterOptions +} + +type ClusterOptions struct { + Name string + Prom string + + BasicAuthUser string + BasicAuthPass string + + Headers []string + + Timeout int64 + DialTimeout int64 + + UseTLS bool + tlsx.ClientConfig + + MaxIdleConnsPerHost int +} + +func Parse(fpath string, configPtr interface{}) error { + var ( + tBuf []byte + ) + loaders := []multiconfig.Loader{ + &multiconfig.TagLoader{}, + &multiconfig.EnvironmentLoader{}, + } + s := cfg.NewFileScanner() + + s.Read(path.Join(fpath)) + tBuf = append(tBuf, s.Data()...) + tBuf = append(tBuf, []byte("\n")...) + + if s.Err() != nil { + return s.Err() + } + + if len(tBuf) != 0 { + loaders = append(loaders, &multiconfig.TOMLLoader{Reader: bytes.NewReader(tBuf)}) + } + + m := multiconfig.DefaultLoader{ + Loader: multiconfig.MultiLoader(loaders...), + Validator: multiconfig.MultiValidator(&multiconfig.RequiredValidator{}), + } + return m.Load(configPtr) +} diff --git a/cli/upgrade/readme.md b/cli/upgrade/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..20e827dbb702745cabcd0e68d1535d0d6fe506a4 --- /dev/null +++ b/cli/upgrade/readme.md @@ -0,0 +1,5 @@ +1. 更新表结构 +2. 读取 webapi.conf 配置 +3. 集群配置写入数据库 +4. 将相关表中的 cluster 转换为 datasoruce_id +5. 将告警规则中存储 runbook 的,存到 annotation 中 \ No newline at end of file diff --git a/cli/upgrade/upgrade.go b/cli/upgrade/upgrade.go new file mode 100644 index 0000000000000000000000000000000000000000..588a5cb3281d768379f438b3c67e1c8151c51c23 --- /dev/null +++ b/cli/upgrade/upgrade.go @@ -0,0 +1,84 @@ +package upgrade + +import ( + "context" + "fmt" + "os/exec" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/storage" + + "github.com/go-sql-driver/mysql" +) + +func Upgrade(configFile string, sqlFile string) error { + var config Config + Parse(configFile, &config) + dsnConf, _ := mysql.ParseDSN(config.DB.DSN) + passwd := dsnConf.Passwd + user := dsnConf.User + + cmd := exec.Command("mysql", fmt.Sprintf("-u%s", user), fmt.Sprintf("-p%s", passwd), "<", sqlFile) + if err := cmd.Run(); err != nil { + return err + } + + db, err := storage.New(config.DB) + if err != nil { + return err + } + ctx := ctx.NewContext(context.Background(), db) + + datasources, err := models.GetDatasources(ctx) + if err != nil { + return err + } + + m := make(map[string]models.Datasource) + for i := 0; i < len(datasources); i++ { + m[datasources[i].Name] = datasources[i] + } + + err = models.AlertRuleUpgradeToV6(ctx, m) + if err != nil { + return err + } + + // alert mute + err = models.AlertMuteUpgradeToV6(ctx, m) + if err != nil { + return err + } + // alert subscribe + err = models.AlertSubscribeUpgradeToV6(ctx, m) + if err != nil { + return err + } + + // alert cur event + err = models.AlertCurEventUpgradeToV6(ctx, m) + if err != nil { + return err + } + + // alert his event + err = models.AlertHisEventUpgradeToV6(ctx, m) + if err != nil { + return err + } + + // target + err = models.TargetUpgradeToV6(ctx, m) + if err != nil { + return err + } + + // recoding rule + err = models.RecordingRuleUpgradeToV6(ctx, m) + if err != nil { + return err + } + + return nil +} diff --git a/cli/upgrade/upgrade.sql b/cli/upgrade/upgrade.sql new file mode 100644 index 0000000000000000000000000000000000000000..2a3c577c2bd5b6cf072535dd5f1c6293439b255e --- /dev/null +++ b/cli/upgrade/upgrade.sql @@ -0,0 +1,94 @@ +use n9e_v5; + +insert into `role_operation`(role_name, operation) values('Guest', '/log/explorer'); +insert into `role_operation`(role_name, operation) values('Guest', '/trace/explorer'); + +insert into `role_operation`(role_name, operation) values('Standard', '/log/explorer'); +insert into `role_operation`(role_name, operation) values('Standard', '/trace/explorer'); +insert into `role_operation`(role_name, operation) values('Standard', '/alert-rules-built-in'); +insert into `role_operation`(role_name, operation) values('Standard', '/dashboards-built-in'); +insert into `role_operation`(role_name, operation) values('Standard', '/trace/dependencies'); + +alter table `board` add built_in tinyint(1) not null default 0 comment '0:false 1:true'; +alter table `board` add hide tinyint(1) not null default 0 comment '0:false 1:true'; + +alter table `chart_share` add datasource_id bigint unsigned not null default 0; + +alter table `alert_rule` add datasource_ids varchar(255) not null default ''; +alter table `alert_rule` add rule_config text not null comment 'rule_config'; +alter table `alert_rule` add annotations text not null comment 'annotations'; + +alter table `alert_mute` add datasource_ids varchar(255) not null default ''; +alter table `alert_mute` add periodic_mutes varchar(4096) not null default ''; +alter table `alert_mute` add mute_time_type tinyint(1) not null default 0; + +alter table `alert_subscribe` add datasource_ids varchar(255) not null default ''; +alter table `alert_subscribe` add prod varchar(255) not null default ''; +alter table `alert_subscribe` add webhooks text; +alter table `alert_subscribe` add redefine_webhooks tinyint(1) default 0; +alter table `alert_subscribe` add for_duration bigint not null default 0; + +alter table `target` add datasource_id bigint unsigned not null default 0; +alter table `target` add offset bigint not null default 0; +alter table `target` modify cluster varchar(128) not null default ''; + +alter table `recording_rule` add datasource_ids varchar(255) default ''; + +alter table `alert_cur_event` add datasource_id bigint unsigned not null default 0; +alter table `alert_cur_event` add annotations text not null comment 'annotations'; +alter table `alert_cur_event` add rule_config text not null comment 'rule_config'; + +alter table `alert_his_event` add datasource_id bigint unsigned not null default 0; +alter table `alert_his_event` add annotations text not null comment 'annotations'; +alter table `alert_his_event` add rule_config text not null comment 'rule_config'; + +alter table alerting_engines add datasource_id bigint unsigned not null default 0; + +CREATE TABLE `datasource` +( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) not null default '', + `description` varchar(255) not null default '', + `category` varchar(255) not null default '', + `plugin_id` int unsigned not null default 0, + `plugin_type` varchar(255) not null default '', + `plugin_type_name` varchar(255) not null default '', + `cluster_name` varchar(255) not null default '', + `settings` text not null, + `status` varchar(255) not null default '', + `http` varchar(4096) not null default '', + `auth` varchar(8192) not null default '', + `created_at` bigint not null default 0, + `created_by` varchar(64) not null default '', + `updated_at` bigint not null default 0, + `updated_by` varchar(64) not null default '', + PRIMARY KEY (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `builtin_cate` ( + `id` bigint unsigned not null auto_increment, + `name` varchar(191) not null, + `user_id` bigint not null default 0, + PRIMARY KEY (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `notify_tpl` ( + `id` bigint unsigned not null auto_increment, + `channel` varchar(32) not null, + `name` varchar(255) not null, + `contact_key` varchar(255) not null, + `contact_key_name` varchar(255) not null, + `hide_contact` int not null default 0, + `hide_channel` int not null default 0, + `content` text not null, + PRIMARY KEY (`id`), + UNIQUE KEY (`channel`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `sso_config` ( + `id` bigint unsigned not null auto_increment, + `name` varchar(255) not null, + `content` text not null, + PRIMARY KEY (`id`), + UNIQUE KEY (`name`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; \ No newline at end of file diff --git a/cmd/alert/main.go b/cmd/alert/main.go new file mode 100644 index 0000000000000000000000000000000000000000..e43b005b5dc0592f6d51aaad9fb49c9f7745ca25 --- /dev/null +++ b/cmd/alert/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "github.com/ccfos/nightingale/v6/alert" + "github.com/ccfos/nightingale/v6/pkg/osx" + "github.com/ccfos/nightingale/v6/pkg/version" + "github.com/toolkits/pkg/runner" +) + +var ( + showVersion = flag.Bool("version", false, "Show version.") + configDir = flag.String("configs", osx.GetEnv("N9E_CONFIGS", "etc"), "Specify configuration directory.(env:N9E_CONFIGS)") + cryptoKey = flag.String("crypto-key", "", "Specify the secret key for configuration file field encryption.") +) + +func main() { + flag.Parse() + + if *showVersion { + fmt.Println(version.Version) + os.Exit(0) + } + + printEnv() + + cleanFunc, err := alert.Initialize(*configDir, *cryptoKey) + if err != nil { + log.Fatalln("failed to initialize:", err) + } + + code := 1 + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + +EXIT: + for { + sig := <-sc + fmt.Println("received signal:", sig.String()) + switch sig { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + code = 0 + break EXIT + case syscall.SIGHUP: + // reload configuration? + default: + break EXIT + } + } + + cleanFunc() + fmt.Println("process exited") + os.Exit(code) +} + +func printEnv() { + runner.Init() + fmt.Println("runner.cwd:", runner.Cwd) + fmt.Println("runner.hostname:", runner.Hostname) + fmt.Println("runner.fd_limits:", runner.FdLimits()) + fmt.Println("runner.vm_limits:", runner.VMLimits()) +} diff --git a/cmd/center/main.go b/cmd/center/main.go new file mode 100644 index 0000000000000000000000000000000000000000..48fb7632d6bb0553df524ed5256f4bd2ec6b0b9f --- /dev/null +++ b/cmd/center/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "github.com/ccfos/nightingale/v6/center" + "github.com/ccfos/nightingale/v6/pkg/osx" + "github.com/ccfos/nightingale/v6/pkg/version" + "github.com/toolkits/pkg/runner" +) + +var ( + showVersion = flag.Bool("version", false, "Show version.") + configDir = flag.String("configs", osx.GetEnv("N9E_CONFIGS", "etc"), "Specify configuration directory.(env:N9E_CONFIGS)") + cryptoKey = flag.String("crypto-key", "", "Specify the secret key for configuration file field encryption.") +) + +func main() { + flag.Parse() + + if *showVersion { + fmt.Println(version.Version) + os.Exit(0) + } + + printEnv() + + cleanFunc, err := center.Initialize(*configDir, *cryptoKey) + if err != nil { + log.Fatalln("failed to initialize:", err) + } + + code := 1 + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + +EXIT: + for { + sig := <-sc + fmt.Println("received signal:", sig.String()) + switch sig { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + code = 0 + break EXIT + case syscall.SIGHUP: + // reload configuration? + default: + break EXIT + } + } + + cleanFunc() + fmt.Println("process exited") + os.Exit(code) +} + +func printEnv() { + runner.Init() + fmt.Println("runner.cwd:", runner.Cwd) + fmt.Println("runner.hostname:", runner.Hostname) + fmt.Println("runner.fd_limits:", runner.FdLimits()) + fmt.Println("runner.vm_limits:", runner.VMLimits()) +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 0000000000000000000000000000000000000000..c630319ee3db36e0e0badcdc2a0e785aa7a0d1f2 --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/ccfos/nightingale/v6/cli" + "github.com/ccfos/nightingale/v6/pkg/version" +) + +var ( + upgrade = flag.Bool("upgrade", false, "Upgrade the database.") + showVersion = flag.Bool("version", false, "Show version.") + sqlFile = flag.String("sql", "", "Specify the sql file to be executed.") + configDir = flag.String("config", "", "Specify configuration directory.(env:N9E_CONFIGS)") +) + +func main() { + flag.Parse() + + if *showVersion { + fmt.Println(version.Version) + os.Exit(0) + } + + if *upgrade { + if *sqlFile == "" { + fmt.Println("Please specify the sql file to be executed.") + os.Exit(1) + } + + if *configDir == "" { + fmt.Println("Please specify the configuration directory.") + os.Exit(1) + } + + err := cli.Upgrade(*configDir, *sqlFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Print("Upgrade successfully.") + os.Exit(0) + } +} diff --git a/cmd/pushgw/main.go b/cmd/pushgw/main.go new file mode 100644 index 0000000000000000000000000000000000000000..55905b1c39d3fd47b9364bfc133df9e379e78492 --- /dev/null +++ b/cmd/pushgw/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "github.com/ccfos/nightingale/v6/pkg/osx" + "github.com/ccfos/nightingale/v6/pkg/version" + "github.com/ccfos/nightingale/v6/pushgw" + + "github.com/toolkits/pkg/runner" +) + +var ( + showVersion = flag.Bool("version", false, "Show version.") + configDir = flag.String("configs", osx.GetEnv("N9E_CONFIGS", "etc"), "Specify configuration directory.(env:N9E_CONFIGS)") + cryptoKey = flag.String("crypto-key", "", "Specify the secret key for configuration file field encryption.") +) + +func main() { + flag.Parse() + + if *showVersion { + fmt.Println(version.Version) + os.Exit(0) + } + + printEnv() + + cleanFunc, err := pushgw.Initialize(*configDir, *cryptoKey) + if err != nil { + log.Fatalln("failed to initialize:", err) + } + + code := 1 + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + +EXIT: + for { + sig := <-sc + fmt.Println("received signal:", sig.String()) + switch sig { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + code = 0 + break EXIT + case syscall.SIGHUP: + // reload configuration? + default: + break EXIT + } + } + + cleanFunc() + fmt.Println("process exited") + os.Exit(code) +} + +func printEnv() { + runner.Init() + fmt.Println("runner.cwd:", runner.Cwd) + fmt.Println("runner.hostname:", runner.Hostname) + fmt.Println("runner.fd_limits:", runner.FdLimits()) + fmt.Println("runner.vm_limits:", runner.VMLimits()) +} diff --git a/conf/conf.go b/conf/conf.go new file mode 100644 index 0000000000000000000000000000000000000000..aa47e14885e6d997b5cab54a964d86f3e2c07a47 --- /dev/null +++ b/conf/conf.go @@ -0,0 +1,80 @@ +package conf + +import ( + "fmt" + "os" + "strings" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/center/cconf" + "github.com/ccfos/nightingale/v6/pkg/cfg" + "github.com/ccfos/nightingale/v6/pkg/httpx" + "github.com/ccfos/nightingale/v6/pkg/logx" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/ccfos/nightingale/v6/pushgw/pconf" + "github.com/ccfos/nightingale/v6/storage" +) + +type ConfigType struct { + Global GlobalConfig + Log logx.Config + HTTP httpx.Config + DB ormx.DBConfig + Redis storage.RedisConfig + + Pushgw pconf.Pushgw + Alert aconf.Alert + Center cconf.Center +} + +type GlobalConfig struct { + RunMode string +} + +func InitConfig(configDir, cryptoKey string) (*ConfigType, error) { + var config = new(ConfigType) + + if err := cfg.LoadConfigByDir(configDir, config); err != nil { + return nil, fmt.Errorf("failed to load configs of directory: %s error: %s", configDir, err) + } + + config.Pushgw.PreCheck() + config.Alert.PreCheck() + config.Center.PreCheck() + + err := decryptConfig(config, cryptoKey) + if err != nil { + return nil, err + } + + if config.Pushgw.DatasourceId == 0 { + return nil, fmt.Errorf("datasourceId is 0") + } + + if config.Alert.Heartbeat.IP == "" { + // auto detect + // config.Alert.Heartbeat.IP = fmt.Sprint(GetOutboundIP()) + // 自动获取IP在有些环境下容易出错,这里用hostname+pid来作唯一标识 + + hostname, err := os.Hostname() + if err != nil { + fmt.Println("failed to get hostname:", err) + os.Exit(1) + } + + if strings.Contains(hostname, "localhost") { + fmt.Println("Warning! hostname contains substring localhost, setting a more unique hostname is recommended") + } + + config.Alert.Heartbeat.IP = hostname + + // if config.Alert.Heartbeat.IP == "" { + // fmt.Println("heartbeat ip auto got is blank") + // os.Exit(1) + // } + } + + config.Alert.Heartbeat.Endpoint = fmt.Sprintf("%s:%d", config.Alert.Heartbeat.IP, config.HTTP.Port) + + return config, nil +} diff --git a/conf/crypto.go b/conf/crypto.go new file mode 100644 index 0000000000000000000000000000000000000000..4e636da63f15d8e93384ed4487f7e2daddcc001c --- /dev/null +++ b/conf/crypto.go @@ -0,0 +1,36 @@ +package conf + +import ( + "fmt" + + "github.com/ccfos/nightingale/v6/pkg/secu" +) + +func decryptConfig(config *ConfigType, cryptoKey string) error { + decryptDsn, err := secu.DealWithDecrypt(config.DB.DSN, cryptoKey) + if err != nil { + return fmt.Errorf("failed to decrypt the db dsn: %s", err) + } + + config.DB.DSN = decryptDsn + + for k := range config.HTTP.BasicAuth { + decryptPwd, err := secu.DealWithDecrypt(config.HTTP.BasicAuth[k], cryptoKey) + if err != nil { + return fmt.Errorf("failed to decrypt http basic auth password: %s", err) + } + + config.HTTP.BasicAuth[k] = decryptPwd + } + + for i, v := range config.Pushgw.Writers { + decryptWriterPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, cryptoKey) + if err != nil { + return fmt.Errorf("failed to decrypt writer basic auth password: %s", err) + } + + config.Pushgw.Writers[i].BasicAuthPass = decryptWriterPwd + } + + return nil +} diff --git a/docker/.dockerignore b/docker/.dockerignore deleted file mode 100644 index 128291fa3b7a450373168f0a3c44128b7411000e..0000000000000000000000000000000000000000 --- a/docker/.dockerignore +++ /dev/null @@ -1,7 +0,0 @@ -ibexetc -initsql -mysqletc -n9eetc -prometc -build.sh -docker-compose.yaml diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 39d449b015085003e7626c2f8d289564421f0e1c..0000000000000000000000000000000000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM python:2.7.8-slim -#FROM python:2 -#FROM ubuntu:21.04 - -WORKDIR /app -ADD n9e /app -ADD http://download.flashcat.cloud/wait /wait -RUN mkdir -p /app/pub && chmod +x /wait -ADD pub /app/pub/ -RUN chmod +x n9e - -EXPOSE 19000 -EXPOSE 18000 - -CMD ["/app/n9e", "-h"] diff --git a/docker/Dockerfile.goreleaser b/docker/Dockerfile.goreleaser deleted file mode 100644 index cf25f56f01f0bfde3434d57febd5a5196c9cc338..0000000000000000000000000000000000000000 --- a/docker/Dockerfile.goreleaser +++ /dev/null @@ -1,14 +0,0 @@ -FROM --platform=$BUILDPLATFORM python:2.7.8-slim - - -WORKDIR /app -ADD n9e /app -ADD http://download.flashcat.cloud/wait /wait -RUN mkdir -p /app/pub && chmod +x /wait -ADD pub /app/pub/ -RUN chmod +x n9e - -EXPOSE 19000 -EXPOSE 18000 - -CMD ["/app/n9e", "-h"] diff --git a/docker/build.sh b/docker/build.sh deleted file mode 100755 index ddc9f3e78bea1329888a60794729c0e597850bb7..0000000000000000000000000000000000000000 --- a/docker/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -if [ $# -ne 1 ]; then - echo "$0 " - exit 0 -fi - -tag=$1 - -echo "tag: ${tag}" - -rm -rf n9e pub -cp ../n9e . -cp -r ../pub . - -docker build -t nightingale:${tag} . - -docker tag nightingale:${tag} ulric2019/nightingale:${tag} -docker push ulric2019/nightingale:${tag} - -rm -rf n9e pub diff --git a/docker/categraf/conf/config.toml b/docker/categraf/conf/config.toml deleted file mode 100644 index 47127cea284fd9d3058c9b65ebad0bb5a8ae2bab..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/config.toml +++ /dev/null @@ -1,51 +0,0 @@ -[global] -# whether print configs -print_configs = false - -# add label(agent_hostname) to series -# "" -> auto detect hostname -# "xx" -> use specified string xx -# "$hostname" -> auto detect hostname -# "$ip" -> auto detect ip -# "$hostname-$ip" -> auto detect hostname and ip to replace the vars -hostname = "$HOSTNAME" - -# will not add label(agent_hostname) if true -omit_hostname = false - -# s | ms -precision = "ms" - -# global collect interval -interval = 15 - -[global.labels] -source="categraf" -# region = "shanghai" -# env = "localhost" - -[writer_opt] -# default: 2000 -batch = 2000 -# channel(as queue) size -chan_size = 10000 - -[[writers]] -url = "http://nserver:19000/prometheus/v1/write" - -# Basic auth username -basic_auth_user = "" - -# Basic auth password -basic_auth_pass = "" - -# timeout settings, unit: ms -timeout = 5000 -dial_timeout = 2500 -max_idle_conns_per_host = 100 - -[http] -enable = false -address = ":9100" -print_access = false -run_mode = "release" diff --git a/docker/categraf/conf/input.cpu/cpu.toml b/docker/categraf/conf/input.cpu/cpu.toml deleted file mode 100644 index 50d511a88909b8c5d255f8c224365a989d9bd68a..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.cpu/cpu.toml +++ /dev/null @@ -1,5 +0,0 @@ -# # collect interval -# interval = 15 - -# # whether collect per cpu -# collect_per_cpu = false diff --git a/docker/categraf/conf/input.disk/disk.toml b/docker/categraf/conf/input.disk/disk.toml deleted file mode 100644 index 72f2563595b19e0a9db10fd132384d32bdeb58ce..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.disk/disk.toml +++ /dev/null @@ -1,11 +0,0 @@ -# # collect interval -# interval = 15 - -# # By default stats will be gathered for all mount points. -# # Set mount_points will restrict the stats to only the specified mount points. -# mount_points = ["/"] - -# Ignore mount points by filesystem type. -ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] - -ignore_mount_points = ["/boot"] diff --git a/docker/categraf/conf/input.diskio/diskio.toml b/docker/categraf/conf/input.diskio/diskio.toml deleted file mode 100644 index 2ed9d56f7532c93702e8c60d0e61b9ef85b43940..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.diskio/diskio.toml +++ /dev/null @@ -1,6 +0,0 @@ -# # collect interval -# interval = 15 - -# # By default, categraf will gather stats for all devices including disk partitions. -# # Setting devices will restrict the stats to the specified devices. -# devices = ["sda", "sdb", "vd*"] \ No newline at end of file diff --git a/docker/categraf/conf/input.docker/docker.toml b/docker/categraf/conf/input.docker/docker.toml deleted file mode 100644 index c2d44d0037c57177029ed9b3467812a5b79b1c02..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.docker/docker.toml +++ /dev/null @@ -1,63 +0,0 @@ -# # collect interval -# interval = 15 - -[[instances]] -# # append some labels for series -# labels = { region="cloud", product="n9e" } - -# # interval = global.interval * interval_times -# interval_times = 1 - -## Docker Endpoint -## To use TCP, set endpoint = "tcp://[ip]:[port]" -## To use environment variables (ie, docker-machine), set endpoint = "ENV" -endpoint = "unix:///var/run/docker.sock" - -## Set to true to collect Swarm metrics(desired_replicas, running_replicas) -gather_services = false -gather_extend_memstats = false - -container_id_label_enable = true -container_id_label_short_style = true - -## Containers to include and exclude. Globs accepted. -## Note that an empty array for both will include all containers -container_name_include = [] -container_name_exclude = [] - -## Container states to include and exclude. Globs accepted. -## When empty only containers in the "running" state will be captured. -## example: container_state_include = ["created", "restarting", "running", "removing", "paused", "exited", "dead"] -## example: container_state_exclude = ["created", "restarting", "running", "removing", "paused", "exited", "dead"] -# container_state_include = [] -# container_state_exclude = [] - -## Timeout for docker list, info, and stats commands -timeout = "5s" - -## Specifies for which classes a per-device metric should be issued -## Possible values are 'cpu' (cpu0, cpu1, ...), 'blkio' (8:0, 8:1, ...) and 'network' (eth0, eth1, ...) -## Please note that this setting has no effect if 'perdevice' is set to 'true' -perdevice_include = [] - -## Specifies for which classes a total metric should be issued. Total is an aggregated of the 'perdevice' values. -## Possible values are 'cpu', 'blkio' and 'network' -## Total 'cpu' is reported directly by Docker daemon, and 'network' and 'blkio' totals are aggregated by this plugin. -## Please note that this setting has no effect if 'total' is set to 'false' -total_include = ["cpu", "blkio", "network"] - -## Which environment variables should we use as a tag -##tag_env = ["JAVA_HOME", "HEAP_SIZE"] - -## docker labels to include and exclude as tags. Globs accepted. -## Note that an empty array for both will include all labels as tags -docker_label_include = [] -docker_label_exclude = ["annotation*", "io.kubernetes*", "*description*", "*maintainer*", "*hash", "*author*"] - -## Optional TLS Config -# use_tls = false -# tls_ca = "/etc/telegraf/ca.pem" -# tls_cert = "/etc/telegraf/cert.pem" -# tls_key = "/etc/telegraf/key.pem" -## Use TLS but skip chain & host verification -# insecure_skip_verify = false diff --git a/docker/categraf/conf/input.kernel/kernel.toml b/docker/categraf/conf/input.kernel/kernel.toml deleted file mode 100644 index b6110f7ec8c4cf2c43c408849c73fe3bc2137a40..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.kernel/kernel.toml +++ /dev/null @@ -1,2 +0,0 @@ -# # collect interval -# interval = 15 diff --git a/docker/categraf/conf/input.kernel_vmstat/kernel_vmstat.toml b/docker/categraf/conf/input.kernel_vmstat/kernel_vmstat.toml deleted file mode 100644 index 68327a8065eb71e409e553f4710c7ddf7034bd41..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.kernel_vmstat/kernel_vmstat.toml +++ /dev/null @@ -1,124 +0,0 @@ -# # collect interval -# interval = 15 - -# file: /proc/vmstat -[white_list] -oom_kill = 1 -nr_free_pages = 0 -nr_alloc_batch = 0 -nr_inactive_anon = 0 -nr_active_anon = 0 -nr_inactive_file = 0 -nr_active_file = 0 -nr_unevictable = 0 -nr_mlock = 0 -nr_anon_pages = 0 -nr_mapped = 0 -nr_file_pages = 0 -nr_dirty = 0 -nr_writeback = 0 -nr_slab_reclaimable = 0 -nr_slab_unreclaimable = 0 -nr_page_table_pages = 0 -nr_kernel_stack = 0 -nr_unstable = 0 -nr_bounce = 0 -nr_vmscan_write = 0 -nr_vmscan_immediate_reclaim = 0 -nr_writeback_temp = 0 -nr_isolated_anon = 0 -nr_isolated_file = 0 -nr_shmem = 0 -nr_dirtied = 0 -nr_written = 0 -numa_hit = 0 -numa_miss = 0 -numa_foreign = 0 -numa_interleave = 0 -numa_local = 0 -numa_other = 0 -workingset_refault = 0 -workingset_activate = 0 -workingset_nodereclaim = 0 -nr_anon_transparent_hugepages = 0 -nr_free_cma = 0 -nr_dirty_threshold = 0 -nr_dirty_background_threshold = 0 -pgpgin = 0 -pgpgout = 0 -pswpin = 0 -pswpout = 0 -pgalloc_dma = 0 -pgalloc_dma32 = 0 -pgalloc_normal = 0 -pgalloc_movable = 0 -pgfree = 0 -pgactivate = 0 -pgdeactivate = 0 -pgfault = 0 -pgmajfault = 0 -pglazyfreed = 0 -pgrefill_dma = 0 -pgrefill_dma32 = 0 -pgrefill_normal = 0 -pgrefill_movable = 0 -pgsteal_kswapd_dma = 0 -pgsteal_kswapd_dma32 = 0 -pgsteal_kswapd_normal = 0 -pgsteal_kswapd_movable = 0 -pgsteal_direct_dma = 0 -pgsteal_direct_dma32 = 0 -pgsteal_direct_normal = 0 -pgsteal_direct_movable = 0 -pgscan_kswapd_dma = 0 -pgscan_kswapd_dma32 = 0 -pgscan_kswapd_normal = 0 -pgscan_kswapd_movable = 0 -pgscan_direct_dma = 0 -pgscan_direct_dma32 = 0 -pgscan_direct_normal = 0 -pgscan_direct_movable = 0 -pgscan_direct_throttle = 0 -zone_reclaim_failed = 0 -pginodesteal = 0 -slabs_scanned = 0 -kswapd_inodesteal = 0 -kswapd_low_wmark_hit_quickly = 0 -kswapd_high_wmark_hit_quickly = 0 -pageoutrun = 0 -allocstall = 0 -pgrotated = 0 -drop_pagecache = 0 -drop_slab = 0 -numa_pte_updates = 0 -numa_huge_pte_updates = 0 -numa_hint_faults = 0 -numa_hint_faults_local = 0 -numa_pages_migrated = 0 -pgmigrate_success = 0 -pgmigrate_fail = 0 -compact_migrate_scanned = 0 -compact_free_scanned = 0 -compact_isolated = 0 -compact_stall = 0 -compact_fail = 0 -compact_success = 0 -htlb_buddy_alloc_success = 0 -htlb_buddy_alloc_fail = 0 -unevictable_pgs_culled = 0 -unevictable_pgs_scanned = 0 -unevictable_pgs_rescued = 0 -unevictable_pgs_mlocked = 0 -unevictable_pgs_munlocked = 0 -unevictable_pgs_cleared = 0 -unevictable_pgs_stranded = 0 -thp_fault_alloc = 0 -thp_fault_fallback = 0 -thp_collapse_alloc = 0 -thp_collapse_alloc_failed = 0 -thp_split = 0 -thp_zero_page_alloc = 0 -thp_zero_page_alloc_failed = 0 -balloon_inflate = 0 -balloon_deflate = 0 -balloon_migrate = 0 diff --git a/docker/categraf/conf/input.linux_sysctl_fs/linux_sysctl_fs.toml b/docker/categraf/conf/input.linux_sysctl_fs/linux_sysctl_fs.toml deleted file mode 100644 index b6110f7ec8c4cf2c43c408849c73fe3bc2137a40..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.linux_sysctl_fs/linux_sysctl_fs.toml +++ /dev/null @@ -1,2 +0,0 @@ -# # collect interval -# interval = 15 diff --git a/docker/categraf/conf/input.mem/mem.toml b/docker/categraf/conf/input.mem/mem.toml deleted file mode 100644 index e03d3c9b55eccb9d7331776dde78f24bc477776a..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.mem/mem.toml +++ /dev/null @@ -1,5 +0,0 @@ -# # collect interval -# interval = 15 - -# # whether collect platform specified metrics -collect_platform_fields = true diff --git a/docker/categraf/conf/input.net/net.toml b/docker/categraf/conf/input.net/net.toml deleted file mode 100644 index 75aae62356d5c7768db2706ea3ce304a94c16811..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.net/net.toml +++ /dev/null @@ -1,8 +0,0 @@ -# # collect interval -# interval = 15 - -# # whether collect protocol stats on Linux -# collect_protocol_stats = false - -# # setting interfaces will tell categraf to gather these explicit interfaces -# interfaces = ["eth0"] \ No newline at end of file diff --git a/docker/categraf/conf/input.netstat/netstat.toml b/docker/categraf/conf/input.netstat/netstat.toml deleted file mode 100644 index b6110f7ec8c4cf2c43c408849c73fe3bc2137a40..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.netstat/netstat.toml +++ /dev/null @@ -1,2 +0,0 @@ -# # collect interval -# interval = 15 diff --git a/docker/categraf/conf/input.processes/processes.toml b/docker/categraf/conf/input.processes/processes.toml deleted file mode 100644 index af7436b61111c999fa1a2d57ea0ee03b52f703f7..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.processes/processes.toml +++ /dev/null @@ -1,8 +0,0 @@ -# # collect interval -# interval = 15 - -# # force use ps command to gather -# force_ps = false - -# # force use /proc to gather -# force_proc = false \ No newline at end of file diff --git a/docker/categraf/conf/input.system/system.toml b/docker/categraf/conf/input.system/system.toml deleted file mode 100644 index c2b6c736ede03e95f2e666c617ecd26361b35246..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/input.system/system.toml +++ /dev/null @@ -1,5 +0,0 @@ -# # collect interval -# interval = 15 - -# # whether collect metric: system_n_users -# collect_user_number = false diff --git a/docker/categraf/conf/logs.toml b/docker/categraf/conf/logs.toml deleted file mode 100644 index a4d8b721cb164bfc272f60e6ac88062c6685ba0f..0000000000000000000000000000000000000000 --- a/docker/categraf/conf/logs.toml +++ /dev/null @@ -1,35 +0,0 @@ -[logs] -## key 占位符 -api_key = "ef4ahfbwzwwtlwfpbertgq1i6mq0ab1q" -## 是否开启日志采集 -enable = false -## 接受日志的server地址 -send_to = "127.0.0.1:17878" -## 发送日志的协议 http/tcp -send_type = "http" -## 是否压缩发送 -use_compress = false -## 是否采用ssl -send_with_tls = false -## -batch_wait = 5 -## 日志offset信息保存目录 -run_path = "/opt/categraf/run" -## 最多同时采集多少个日志文件 -open_files_limit = 100 -## 定期扫描目录下是否有新增日志 -scan_period = 10 -## -frame_size = 10 -## -collect_container_all = true - ## 全局的处理规则 - [[logs.Processing_rules]] - ## 单个日志采集配置 - [[logs.items]] - ## file/journald - type = "file" - ## type=file时 path必填,type=journald时 port必填 - path = "/opt/tomcat/logs/*.txt" - source = "tomcat" - service = "my_service" diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml deleted file mode 100644 index 504e20311b4b4afac0f7fddd64aeed2002a464ca..0000000000000000000000000000000000000000 --- a/docker/docker-compose.yaml +++ /dev/null @@ -1,179 +0,0 @@ -version: "3.7" - -networks: - nightingale: - driver: bridge - -services: - mysql: - platform: linux/x86_64 - image: "mysql:5.7" - container_name: mysql - hostname: mysql - restart: always - ports: - - "3306:3306" - environment: - TZ: Asia/Shanghai - MYSQL_ROOT_PASSWORD: 1234 - volumes: - - ./mysqldata:/var/lib/mysql/ - - ./initsql:/docker-entrypoint-initdb.d/ - - ./mysqletc/my.cnf:/etc/my.cnf - networks: - - nightingale - - redis: - image: "redis:6.2" - container_name: redis - hostname: redis - restart: always - ports: - - "6379:6379" - environment: - TZ: Asia/Shanghai - networks: - - nightingale - - prometheus: - image: prom/prometheus - container_name: prometheus - hostname: prometheus - restart: always - environment: - TZ: Asia/Shanghai - volumes: - - ./prometc:/etc/prometheus - ports: - - "9090:9090" - networks: - - nightingale - command: - - "--config.file=/etc/prometheus/prometheus.yml" - - "--storage.tsdb.path=/prometheus" - - "--web.console.libraries=/usr/share/prometheus/console_libraries" - - "--web.console.templates=/usr/share/prometheus/consoles" - - "--enable-feature=remote-write-receiver" - - "--query.lookback-delta=2m" - - ibex: - image: ulric2019/ibex:0.3 - container_name: ibex - hostname: ibex - restart: always - environment: - GIN_MODE: release - TZ: Asia/Shanghai - WAIT_HOSTS: mysql:3306 - ports: - - "10090:10090" - - "20090:20090" - volumes: - - ./ibexetc:/app/etc - networks: - - nightingale - depends_on: - - mysql - links: - - mysql:mysql - command: > - sh -c "/wait && /app/ibex server" - - nwebapi: - image: flashcatcloud/nightingale:latest - container_name: nwebapi - hostname: nwebapi - restart: always - environment: - GIN_MODE: release - TZ: Asia/Shanghai - WAIT_HOSTS: mysql:3306, redis:6379 - volumes: - - ./n9eetc:/app/etc - ports: - - "18000:18000" - networks: - - nightingale - depends_on: - - mysql - - redis - - prometheus - - ibex - links: - - mysql:mysql - - redis:redis - - prometheus:prometheus - - ibex:ibex - command: > - sh -c "/wait && /app/n9e webapi" - - nserver: - image: flashcatcloud/nightingale:latest - container_name: nserver - hostname: nserver - restart: always - environment: - GIN_MODE: release - TZ: Asia/Shanghai - WAIT_HOSTS: mysql:3306, redis:6379 - volumes: - - ./n9eetc:/app/etc - ports: - - "19000:19000" - networks: - - nightingale - depends_on: - - mysql - - redis - - prometheus - - ibex - links: - - mysql:mysql - - redis:redis - - prometheus:prometheus - - ibex:ibex - command: > - sh -c "/wait && /app/n9e server" - - categraf: - image: "flashcatcloud/categraf:latest" - container_name: "categraf" - hostname: "categraf01" - restart: always - environment: - TZ: Asia/Shanghai - HOST_PROC: /hostfs/proc - HOST_SYS: /hostfs/sys - HOST_MOUNT_PREFIX: /hostfs - volumes: - - ./categraf/conf:/etc/categraf/conf - - /:/hostfs - - /var/run/docker.sock:/var/run/docker.sock - ports: - - "9100:9100/tcp" - networks: - - nightingale - depends_on: - - nserver - links: - - nserver:nserver - - agentd: - image: ulric2019/ibex:0.3 - container_name: agentd - hostname: agentd - restart: always - environment: - GIN_MODE: release - TZ: Asia/Shanghai - volumes: - - ./ibexetc:/app/etc - networks: - - nightingale - depends_on: - - ibex - links: - - ibex:ibex - command: - - "/app/ibex" - - "agentd" diff --git a/docker/ibexetc/agentd.conf b/docker/ibexetc/agentd.conf deleted file mode 100644 index 86b6d07c2f8bb99cb39b4327e4deb8583d81cc95..0000000000000000000000000000000000000000 --- a/docker/ibexetc/agentd.conf +++ /dev/null @@ -1,38 +0,0 @@ -# debug, release -RunMode = "release" - -# task meta storage dir -MetaDir = "./meta" - -[HTTP] -Enable = true -# http listening address -Host = "0.0.0.0" -# http listening port -Port = 2090 -# https cert file path -CertFile = "" -# https key file path -KeyFile = "" -# whether print access log -PrintAccessLog = true -# whether enable pprof -PProf = false -# http graceful shutdown timeout, unit: s -ShutdownTimeout = 30 -# max content length: 64M -MaxContentLength = 67108864 -# http server read timeout, unit: s -ReadTimeout = 20 -# http server write timeout, unit: s -WriteTimeout = 40 -# http server idle timeout, unit: s -IdleTimeout = 120 - -[Heartbeat] -# unit: ms -Interval = 1000 -# rpc servers -Servers = ["ibex:20090"] -# $ip or $hostname or specified string -Host = "categraf01" \ No newline at end of file diff --git a/docker/ibexetc/server.conf b/docker/ibexetc/server.conf deleted file mode 100644 index f8b10af915870a784838afdda65255edd4f1fa9c..0000000000000000000000000000000000000000 --- a/docker/ibexetc/server.conf +++ /dev/null @@ -1,97 +0,0 @@ -# debug, release -RunMode = "release" - -[Log] -# log write dir -Dir = "logs-server" -# log level: DEBUG INFO WARNING ERROR -Level = "DEBUG" -# stdout, stderr, file -Output = "stdout" -# # rotate by time -# KeepHours: 4 -# # rotate by size -# RotateNum = 3 -# # unit: MB -# RotateSize = 256 - -[HTTP] -Enable = true -# http listening address -Host = "0.0.0.0" -# http listening port -Port = 10090 -# https cert file path -CertFile = "" -# https key file path -KeyFile = "" -# whether print access log -PrintAccessLog = true -# whether enable pprof -PProf = false -# http graceful shutdown timeout, unit: s -ShutdownTimeout = 30 -# max content length: 64M -MaxContentLength = 67108864 -# http server read timeout, unit: s -ReadTimeout = 20 -# http server write timeout, unit: s -WriteTimeout = 40 -# http server idle timeout, unit: s -IdleTimeout = 120 - -[BasicAuth] -# using when call apis -ibex = "ibex" - -[RPC] -Listen = "0.0.0.0:20090" - -[Heartbeat] -# auto detect if blank -IP = "" -# unit: ms -Interval = 1000 - -[Output] -# database | remote -ComeFrom = "database" -AgtdPort = 2090 - -[Gorm] -# enable debug mode or not -Debug = false -# mysql postgres -DBType = "mysql" -# unit: s -MaxLifetime = 7200 -# max open connections -MaxOpenConns = 150 -# max idle connections -MaxIdleConns = 50 -# table prefix -TablePrefix = "" - -[MySQL] -# mysql address host:port -Address = "mysql:3306" -# mysql username -User = "root" -# mysql password -Password = "1234" -# database name -DBName = "ibex" -# connection params -Parameters = "charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true" - -[Postgres] -# pg address host:port -Address = "postgres:5432" -# pg user -User = "root" -# pg password -Password = "1234" -# database name -DBName = "ibex" -# ssl mode -SSLMode = "disable" diff --git a/docker/initsql/b-ibex.sql b/docker/initsql/b-ibex.sql deleted file mode 100644 index 220ee7c11fca1837a7fa8cde68d4500c12bf7250..0000000000000000000000000000000000000000 --- a/docker/initsql/b-ibex.sql +++ /dev/null @@ -1,1362 +0,0 @@ -set names utf8mb4; - -drop database if exists ibex; -create database ibex; -use ibex; - -CREATE TABLE `task_meta` -( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(255) not null default '', - `account` varchar(64) not null, - `batch` int unsigned not null default 0, - `tolerance` int unsigned not null default 0, - `timeout` int unsigned not null default 0, - `pause` varchar(255) not null default '', - `script` text not null, - `args` varchar(512) not null default '', - `creator` varchar(64) not null default '', - `created` timestamp not null default CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY (`creator`), - KEY (`created`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -/* start|cancel|kill|pause */ -CREATE TABLE `task_action` -( - `id` bigint unsigned not null, - `action` varchar(32) not null, - `clock` bigint not null default 0, - PRIMARY KEY (`id`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE `task_scheduler` -( - `id` bigint unsigned not null, - `scheduler` varchar(128) not null default '', - KEY (`id`, `scheduler`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE `task_scheduler_health` -( - `scheduler` varchar(128) not null, - `clock` bigint not null, - UNIQUE KEY (`scheduler`), - KEY (`clock`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE `task_host_doing` -( - `id` bigint unsigned not null, - `host` varchar(128) not null, - `clock` bigint not null default 0, - `action` varchar(16) not null, - KEY (`id`), - KEY (`host`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_0 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_1 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_2 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_3 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_4 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_5 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_6 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_7 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_8 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_9 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_10 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_11 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_12 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_13 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_14 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_15 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_16 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_17 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_18 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_19 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_20 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_21 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_22 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_23 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_24 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_25 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_26 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_27 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_28 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_29 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_30 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_31 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_32 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_33 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_34 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_35 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_36 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_37 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_38 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_39 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_40 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_41 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_42 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_43 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_44 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_45 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_46 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_47 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_48 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_49 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_50 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_51 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_52 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_53 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_54 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_55 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_56 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_57 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_58 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_59 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_60 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_61 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_62 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_63 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_64 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_65 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_66 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_67 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_68 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_69 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_70 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_71 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_72 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_73 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_74 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_75 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_76 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_77 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_78 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_79 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_80 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_81 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_82 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_83 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_84 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_85 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_86 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_87 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_88 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_89 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_90 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_91 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_92 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_93 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_94 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_95 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_96 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_97 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_98 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; - -CREATE TABLE task_host_99 -( - `ii` bigint unsigned NOT NULL AUTO_INCREMENT, - `id` bigint unsigned not null, - `host` varchar(128) not null, - `status` varchar(32) not null, - `stdout` text, - `stderr` text, - UNIQUE KEY (`id`, `host`), - PRIMARY KEY (`ii`) -) ENGINE = InnoDB - DEFAULT CHARSET = utf8mb4; diff --git a/docker/initsql/c-init.sql b/docker/initsql/c-init.sql deleted file mode 100644 index f5f69b8a2e139f26c44616c38b299277b2a6ed0f..0000000000000000000000000000000000000000 --- a/docker/initsql/c-init.sql +++ /dev/null @@ -1,3 +0,0 @@ -GRANT ALL ON *.* TO 'root'@'127.0.0.1' IDENTIFIED BY '1234'; -GRANT ALL ON *.* TO 'root'@'localhost' IDENTIFIED BY '1234'; -GRANT ALL ON *.* TO 'root'@'%' IDENTIFIED BY '1234'; \ No newline at end of file diff --git a/docker/initsql_for_postgres/a-n9e-for-Postgres.sql b/docker/initsql_for_postgres/a-n9e-for-Postgres.sql deleted file mode 100644 index e67251276f59cbca4dc01a976cc10f59dd24441a..0000000000000000000000000000000000000000 --- a/docker/initsql_for_postgres/a-n9e-for-Postgres.sql +++ /dev/null @@ -1,614 +0,0 @@ --- n9e version 5.8 for postgres -CREATE TABLE users ( - id bigserial, - username varchar(64) not null , - nickname varchar(64) not null , - password varchar(128) not null default '', - phone varchar(16) not null default '', - email varchar(64) not null default '', - portrait varchar(255) not null default '' , - roles varchar(255) not null , - contacts varchar(1024) , - maintainer smallint not null default 0, - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE users ADD CONSTRAINT users_pk PRIMARY KEY (id); -ALTER TABLE users ADD CONSTRAINT users_un UNIQUE (username); - -COMMENT ON COLUMN users.username IS 'login name, cannot rename'; -COMMENT ON COLUMN users.nickname IS 'display name, chinese name'; -COMMENT ON COLUMN users.portrait IS 'portrait image url'; -COMMENT ON COLUMN users.roles IS 'Admin | Standard | Guest, split by space'; -COMMENT ON COLUMN users.contacts IS 'json e.g. {wecom:xx, dingtalk_robot_token:yy}'; - -insert into users(id, username, nickname, password, roles, create_at, create_by, update_at, update_by) values(1, 'root', '超管', 'root.2020', 'Admin', date_part('epoch',current_timestamp)::int, 'system', date_part('epoch',current_timestamp)::int, 'system'); - - -CREATE TABLE user_group ( - id bigserial, - name varchar(128) not null default '', - note varchar(255) not null default '', - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE user_group ADD CONSTRAINT user_group_pk PRIMARY KEY (id); -CREATE INDEX user_group_create_by_idx ON user_group (create_by); -CREATE INDEX user_group_update_at_idx ON user_group (update_at); - -insert into user_group(id, name, create_at, create_by, update_at, update_by) values(1, 'demo-root-group', date_part('epoch',current_timestamp)::int, 'root', date_part('epoch',current_timestamp)::int, 'root'); - -CREATE TABLE user_group_member ( - id bigserial, - group_id bigint not null, - user_id bigint not null -) ; -ALTER TABLE user_group_member ADD CONSTRAINT user_group_member_pk PRIMARY KEY (id); -CREATE INDEX user_group_member_group_id_idx ON user_group_member (group_id); -CREATE INDEX user_group_member_user_id_idx ON user_group_member (user_id); - -insert into user_group_member(group_id, user_id) values(1, 1); - -CREATE TABLE configs ( - id bigserial, - ckey varchar(191) not null, - cval varchar(4096) not null default '' -) ; -ALTER TABLE configs ADD CONSTRAINT configs_pk PRIMARY KEY (id); -ALTER TABLE configs ADD CONSTRAINT configs_un UNIQUE (ckey); - -CREATE TABLE role ( - id bigserial, - name varchar(191) not null default '', - note varchar(255) not null default '' -) ; -ALTER TABLE role ADD CONSTRAINT role_pk PRIMARY KEY (id); -ALTER TABLE role ADD CONSTRAINT role_un UNIQUE ("name"); - -insert into role(name, note) values('Admin', 'Administrator role'); -insert into role(name, note) values('Standard', 'Ordinary user role'); -insert into role(name, note) values('Guest', 'Readonly user role'); - -CREATE TABLE role_operation( - id bigserial, - role_name varchar(128) not null, - operation varchar(191) not null -) ; -ALTER TABLE role_operation ADD CONSTRAINT role_operation_pk PRIMARY KEY (id); -CREATE INDEX role_operation_role_name_idx ON role_operation (role_name); -CREATE INDEX role_operation_operation_idx ON role_operation (operation); - - --- Admin is special, who has no concrete operation but can do anything. -insert into role_operation(role_name, operation) values('Guest', '/metric/explorer'); -insert into role_operation(role_name, operation) values('Guest', '/object/explorer'); -insert into role_operation(role_name, operation) values('Guest', '/help/version'); -insert into role_operation(role_name, operation) values('Guest', '/help/contact'); -insert into role_operation(role_name, operation) values('Standard', '/metric/explorer'); -insert into role_operation(role_name, operation) values('Standard', '/object/explorer'); -insert into role_operation(role_name, operation) values('Standard', '/help/version'); -insert into role_operation(role_name, operation) values('Standard', '/help/contact'); -insert into role_operation(role_name, operation) values('Standard', '/users'); -insert into role_operation(role_name, operation) values('Standard', '/user-groups'); -insert into role_operation(role_name, operation) values('Standard', '/user-groups/add'); -insert into role_operation(role_name, operation) values('Standard', '/user-groups/put'); -insert into role_operation(role_name, operation) values('Standard', '/user-groups/del'); -insert into role_operation(role_name, operation) values('Standard', '/busi-groups'); -insert into role_operation(role_name, operation) values('Standard', '/busi-groups/add'); -insert into role_operation(role_name, operation) values('Standard', '/busi-groups/put'); -insert into role_operation(role_name, operation) values('Standard', '/busi-groups/del'); -insert into role_operation(role_name, operation) values('Standard', '/targets'); -insert into role_operation(role_name, operation) values('Standard', '/targets/add'); -insert into role_operation(role_name, operation) values('Standard', '/targets/put'); -insert into role_operation(role_name, operation) values('Standard', '/targets/del'); -insert into role_operation(role_name, operation) values('Standard', '/dashboards'); -insert into role_operation(role_name, operation) values('Standard', '/dashboards/add'); -insert into role_operation(role_name, operation) values('Standard', '/dashboards/put'); -insert into role_operation(role_name, operation) values('Standard', '/dashboards/del'); -insert into role_operation(role_name, operation) values('Standard', '/alert-rules'); -insert into role_operation(role_name, operation) values('Standard', '/alert-rules/add'); -insert into role_operation(role_name, operation) values('Standard', '/alert-rules/put'); -insert into role_operation(role_name, operation) values('Standard', '/alert-rules/del'); -insert into role_operation(role_name, operation) values('Standard', '/alert-mutes'); -insert into role_operation(role_name, operation) values('Standard', '/alert-mutes/add'); -insert into role_operation(role_name, operation) values('Standard', '/alert-mutes/del'); -insert into role_operation(role_name, operation) values('Standard', '/alert-subscribes'); -insert into role_operation(role_name, operation) values('Standard', '/alert-subscribes/add'); -insert into role_operation(role_name, operation) values('Standard', '/alert-subscribes/put'); -insert into role_operation(role_name, operation) values('Standard', '/alert-subscribes/del'); -insert into role_operation(role_name, operation) values('Standard', '/alert-cur-events'); -insert into role_operation(role_name, operation) values('Standard', '/alert-cur-events/del'); -insert into role_operation(role_name, operation) values('Standard', '/alert-his-events'); -insert into role_operation(role_name, operation) values('Standard', '/job-tpls'); -insert into role_operation(role_name, operation) values('Standard', '/job-tpls/add'); -insert into role_operation(role_name, operation) values('Standard', '/job-tpls/put'); -insert into role_operation(role_name, operation) values('Standard', '/job-tpls/del'); -insert into role_operation(role_name, operation) values('Standard', '/job-tasks'); -insert into role_operation(role_name, operation) values('Standard', '/job-tasks/add'); -insert into role_operation(role_name, operation) values('Standard', '/job-tasks/put'); -insert into role_operation(role_name, operation) values('Standard', '/recording-rules'); -insert into role_operation(role_name, operation) values('Standard', '/recording-rules/add'); -insert into role_operation(role_name, operation) values('Standard', '/recording-rules/put'); -insert into role_operation(role_name, operation) values('Standard', '/recording-rules/del'); - -CREATE TABLE recording_rule ( - id bigserial NOT NULL, - group_id bigint not null default 0, - cluster varchar(128) not null, - name varchar(255) not null, - note varchar(255) not null, - disabled smallint not null, - prom_ql varchar(8192) not null, - prom_eval_interval int not null, - append_tags varchar(255) default '', - create_at bigint default 0, - create_by varchar(64) default '', - update_at bigint default 0, - update_by varchar(64) default '', - CONSTRAINT recording_rule_pk PRIMARY KEY (id) -) ; -CREATE INDEX recording_rule_group_id_idx ON recording_rule (group_id); -CREATE INDEX recording_rule_update_at_idx ON recording_rule (update_at); -COMMENT ON COLUMN recording_rule.group_id IS 'group_id'; -COMMENT ON COLUMN recording_rule.name IS 'new metric name'; -COMMENT ON COLUMN recording_rule.note IS 'rule note'; -COMMENT ON COLUMN recording_rule.disabled IS '0:enabled 1:disabled'; -COMMENT ON COLUMN recording_rule.prom_ql IS 'promql'; -COMMENT ON COLUMN recording_rule.prom_eval_interval IS 'evaluate interval'; -COMMENT ON COLUMN recording_rule.append_tags IS 'split by space: service=n9e mod=api'; - --- for alert_rule | collect_rule | mute | dashboard grouping -CREATE TABLE busi_group ( - id bigserial, - name varchar(191) not null, - label_enable smallint not null default 0, - label_value varchar(191) not null default '' , - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE busi_group ADD CONSTRAINT busi_group_pk PRIMARY KEY (id); -ALTER TABLE busi_group ADD CONSTRAINT busi_group_un UNIQUE ("name"); -COMMENT ON COLUMN busi_group.label_value IS 'if label_enable: label_value can not be blank'; - -insert into busi_group(id, name, create_at, create_by, update_at, update_by) values(1, 'Default Busi Group', date_part('epoch',current_timestamp)::int, 'root', date_part('epoch',current_timestamp)::int, 'root'); - -CREATE TABLE busi_group_member ( - id bigserial, - busi_group_id bigint not null , - user_group_id bigint not null , - perm_flag char(2) not null -) ; -ALTER TABLE busi_group_member ADD CONSTRAINT busi_group_member_pk PRIMARY KEY (id); -CREATE INDEX busi_group_member_busi_group_id_idx ON busi_group_member (busi_group_id); -CREATE INDEX busi_group_member_user_group_id_idx ON busi_group_member (user_group_id); -COMMENT ON COLUMN busi_group_member.busi_group_id IS 'busi group id'; -COMMENT ON COLUMN busi_group_member.user_group_id IS 'user group id'; -COMMENT ON COLUMN busi_group_member.perm_flag IS 'ro | rw'; - -insert into busi_group_member(busi_group_id, user_group_id, perm_flag) values(1, 1, 'rw'); - --- for dashboard new version -CREATE TABLE board ( - id bigserial not null , - group_id bigint not null default 0 , - name varchar(191) not null, - ident varchar(200) not null default '', - tags varchar(255) not null , - public smallint not null default 0, - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE board ADD CONSTRAINT board_pk PRIMARY KEY (id); -ALTER TABLE board ADD CONSTRAINT board_un UNIQUE (group_id,"name"); -COMMENT ON COLUMN board.group_id IS 'busi group id'; -COMMENT ON COLUMN board.tags IS 'split by space'; -COMMENT ON COLUMN board.public IS '0:false 1:true'; -CREATE INDEX board_ident_idx ON board (ident); - --- for dashboard new version -CREATE TABLE board_payload ( - id bigint not null , - payload text not null -); -ALTER TABLE board_payload ADD CONSTRAINT board_payload_un UNIQUE (id); -COMMENT ON COLUMN board_payload.id IS 'dashboard id'; - --- deprecated -CREATE TABLE dashboard ( - id bigserial, - group_id bigint not null default 0 , - name varchar(191) not null, - tags varchar(255) not null , - configs varchar(8192) , - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE dashboard ADD CONSTRAINT dashboard_pk PRIMARY KEY (id); - --- deprecated --- auto create the first subclass 'Default chart group' of dashboard -CREATE TABLE chart_group ( - id bigserial, - dashboard_id bigint not null, - name varchar(255) not null, - weight int not null default 0 -) ; -ALTER TABLE chart_group ADD CONSTRAINT chart_group_pk PRIMARY KEY (id); - --- deprecated -CREATE TABLE chart ( - id bigserial, - group_id bigint not null , - configs text, - weight int not null default 0 -) ; -ALTER TABLE chart ADD CONSTRAINT chart_pk PRIMARY KEY (id); - -CREATE TABLE chart_share ( - id bigserial, - cluster varchar(128) not null, - configs text, - create_at bigint not null default 0, - create_by varchar(64) not null default '' -) ; -ALTER TABLE chart_share ADD CONSTRAINT chart_share_pk PRIMARY KEY (id); -CREATE INDEX chart_share_create_at_idx ON chart_share (create_at); - -CREATE TABLE alert_rule ( - id bigserial NOT NULL, - group_id int8 NOT NULL DEFAULT 0, - cate varchar(128) not null default '' , - "cluster" varchar(128) NOT NULL, - "name" varchar(255) NOT NULL, - note varchar(1024) NOT NULL, - severity int2 NOT NULL, - disabled int2 NOT NULL, - prom_for_duration int4 NOT NULL, - prom_ql text NOT NULL, - prom_eval_interval int4 NOT NULL, - enable_stime bpchar(5) NOT NULL DEFAULT '00:00'::bpchar, - enable_etime bpchar(5) NOT NULL DEFAULT '23:59'::bpchar, - enable_days_of_week varchar(32) NOT NULL DEFAULT ''::character varying, - enable_in_bg int2 NOT NULL DEFAULT 0, - notify_recovered int2 NOT NULL, - notify_channels varchar(255) NOT NULL DEFAULT ''::character varying, - notify_groups varchar(255) NOT NULL DEFAULT ''::character varying, - notify_repeat_step int4 NOT NULL DEFAULT 0, - notify_max_number int4 not null default 0, - recover_duration int4 NOT NULL DEFAULT 0, - callbacks varchar(255) NOT NULL DEFAULT ''::character varying, - runbook_url varchar(255) NULL, - append_tags varchar(255) NOT NULL DEFAULT ''::character varying, - create_at int8 NOT NULL DEFAULT 0, - create_by varchar(64) NOT NULL DEFAULT ''::character varying, - update_at int8 NOT NULL DEFAULT 0, - update_by varchar(64) NOT NULL DEFAULT ''::character varying, - prod varchar(255) NOT NULL DEFAULT ''::character varying, - algorithm varchar(255) NOT NULL DEFAULT ''::character varying, - algo_params varchar(255) NULL, - delay int4 NOT NULL DEFAULT 0, - CONSTRAINT alert_rule_pk PRIMARY KEY (id) -); -CREATE INDEX alert_rule_group_id_idx ON alert_rule USING btree (group_id); -CREATE INDEX alert_rule_update_at_idx ON alert_rule USING btree (update_at); - -COMMENT ON COLUMN alert_rule.group_id IS 'busi group id'; -COMMENT ON COLUMN alert_rule.severity IS '0:Emergency 1:Warning 2:Notice'; -COMMENT ON COLUMN alert_rule.disabled IS '0:enabled 1:disabled'; -COMMENT ON COLUMN alert_rule.prom_for_duration IS 'prometheus for, unit:s'; -COMMENT ON COLUMN alert_rule.prom_ql IS 'promql'; -COMMENT ON COLUMN alert_rule.prom_eval_interval IS 'evaluate interval'; -COMMENT ON COLUMN alert_rule.enable_stime IS '00:00'; -COMMENT ON COLUMN alert_rule.enable_etime IS '23:59'; -COMMENT ON COLUMN alert_rule.enable_days_of_week IS 'split by space: 0 1 2 3 4 5 6'; -COMMENT ON COLUMN alert_rule.enable_in_bg IS '1: only this bg 0: global'; -COMMENT ON COLUMN alert_rule.notify_recovered IS 'whether notify when recovery'; -COMMENT ON COLUMN alert_rule.notify_channels IS 'split by space: sms voice email dingtalk wecom'; -COMMENT ON COLUMN alert_rule.notify_groups IS 'split by space: 233 43'; -COMMENT ON COLUMN alert_rule.notify_repeat_step IS 'unit: min'; -COMMENT ON COLUMN alert_rule.recover_duration IS 'unit: s'; -COMMENT ON COLUMN alert_rule.callbacks IS 'split by space: http://a.com/api/x http://a.com/api/y'; -COMMENT ON COLUMN alert_rule.append_tags IS 'split by space: service=n9e mod=api'; - -CREATE TABLE alert_mute ( - id bigserial, - group_id bigint not null default 0 , - cate varchar(128) not null default '' , - prod varchar(255) NOT NULL DEFAULT '' , - note varchar(1024) not null default '', - cluster varchar(128) not null, - tags varchar(4096) not null default '' , - cause varchar(255) not null default '', - btime bigint not null default 0 , - etime bigint not null default 0 , - disabled smallint not null default 0 , - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE alert_mute ADD CONSTRAINT alert_mute_pk PRIMARY KEY (id); -CREATE INDEX alert_mute_create_at_idx ON alert_mute (create_at); -CREATE INDEX alert_mute_group_id_idx ON alert_mute (group_id); -COMMENT ON COLUMN alert_mute.group_id IS 'busi group id'; -COMMENT ON COLUMN alert_mute.tags IS 'json,map,tagkey->regexp|value'; -COMMENT ON COLUMN alert_mute.btime IS 'begin time'; -COMMENT ON COLUMN alert_mute.etime IS 'end time'; -COMMENT ON COLUMN alert_mute.disabled IS '0:enabled 1:disabled'; - -CREATE TABLE alert_subscribe ( - id bigserial, - "name" varchar(255) NOT NULL default '', - disabled int2 NOT NULL default 0 , - group_id bigint not null default 0 , - cate varchar(128) not null default '' , - cluster varchar(128) not null, - rule_id bigint not null default 0, - tags jsonb not null , - redefine_severity smallint default 0 , - new_severity smallint not null , - redefine_channels smallint default 0 , - new_channels varchar(255) not null default '' , - user_group_ids varchar(250) not null , - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE alert_subscribe ADD CONSTRAINT alert_subscribe_pk PRIMARY KEY (id); -CREATE INDEX alert_subscribe_group_id_idx ON alert_subscribe (group_id); -CREATE INDEX alert_subscribe_update_at_idx ON alert_subscribe (update_at); -COMMENT ON COLUMN alert_subscribe.disabled IS '0:enabled 1:disabled'; -COMMENT ON COLUMN alert_subscribe.group_id IS 'busi group id'; -COMMENT ON COLUMN alert_subscribe.tags IS 'json,map,tagkey->regexp|value'; -COMMENT ON COLUMN alert_subscribe.redefine_severity IS 'is redefine severity?'; -COMMENT ON COLUMN alert_subscribe.new_severity IS '0:Emergency 1:Warning 2:Notice'; -COMMENT ON COLUMN alert_subscribe.redefine_channels IS 'is redefine channels?'; -COMMENT ON COLUMN alert_subscribe.new_channels IS 'split by space: sms voice email dingtalk wecom'; -COMMENT ON COLUMN alert_subscribe.user_group_ids IS 'split by space 1 34 5, notify cc to user_group_ids'; - - -CREATE TABLE target ( - id bigserial, - group_id bigint not null default 0 , - cluster varchar(128) not null , - ident varchar(191) not null , - note varchar(255) not null default '' , - tags varchar(512) not null default '' , - update_at bigint not null default 0 -) ; -ALTER TABLE target ADD CONSTRAINT target_pk PRIMARY KEY (id); -ALTER TABLE target ADD CONSTRAINT target_un UNIQUE (ident); -CREATE INDEX target_group_id_idx ON target (group_id); - -COMMENT ON COLUMN target.group_id IS 'busi group id'; -COMMENT ON COLUMN target."cluster" IS 'append to alert event as field'; -COMMENT ON COLUMN target.ident IS 'target id'; -COMMENT ON COLUMN target.note IS 'append to alert event as field'; -COMMENT ON COLUMN target.tags IS 'append to series data as tags, split by space, append external space at suffix'; - - -CREATE TABLE metric_view ( - id bigserial, - name varchar(191) not null default '', - cate smallint not null , - configs varchar(8192) not null default '', - create_at bigint not null default 0, - create_by bigint not null default 0 , - update_at bigint not null default 0 -) ; -ALTER TABLE metric_view ADD CONSTRAINT metric_view_pk PRIMARY KEY (id); -CREATE INDEX metric_view_create_by_idx ON metric_view (create_by); - -COMMENT ON COLUMN metric_view.cate IS '0: preset 1: custom'; -COMMENT ON COLUMN metric_view.create_by IS 'user id'; - -insert into metric_view(name, cate, configs) values('Host View', 0, '{"filters":[{"oper":"=","label":"__name__","value":"cpu_usage_idle"}],"dynamicLabels":[],"dimensionLabels":[{"label":"ident","value":""}]}'); - -CREATE TABLE alert_aggr_view ( - id bigserial, - name varchar(191) not null default '', - rule varchar(2048) not null default '', - cate smallint not null , - create_at bigint not null default 0, - create_by bigint not null default 0 , - update_at bigint not null default 0 -) ; -ALTER TABLE alert_aggr_view ADD CONSTRAINT alert_aggr_view_pk PRIMARY KEY (id); -CREATE INDEX alert_aggr_view_create_by_idx ON alert_aggr_view (create_by); - -COMMENT ON COLUMN alert_aggr_view.cate IS '0: preset 1: custom'; -COMMENT ON COLUMN alert_aggr_view.create_by IS 'user id'; - -insert into alert_aggr_view(name, rule, cate) values('By BusiGroup, Severity', 'field:group_name::field:severity', 0); -insert into alert_aggr_view(name, rule, cate) values('By RuleName', 'field:rule_name', 0); - -CREATE TABLE alert_cur_event ( - id bigserial NOT NULL, - cate varchar(128) not null default '' , - "cluster" varchar(128) NOT NULL, - group_id int8 NOT NULL, - group_name varchar(255) NOT NULL DEFAULT ''::character varying, - hash varchar(64) NOT NULL, - rule_id int8 NOT NULL, - rule_name varchar(255) NOT NULL, - rule_note varchar(2048) NOT NULL DEFAULT 'alert rule note'::character varying, - severity int2 NOT NULL, - prom_for_duration int4 NOT NULL, - prom_ql varchar(8192) NOT NULL, - prom_eval_interval int4 NOT NULL, - callbacks varchar(255) NOT NULL DEFAULT ''::character varying, - runbook_url varchar(255) NULL, - notify_recovered int2 NOT NULL, - notify_channels varchar(255) NOT NULL DEFAULT ''::character varying, - notify_groups varchar(255) NOT NULL DEFAULT ''::character varying, - notify_repeat_next int8 NOT NULL DEFAULT 0, - notify_cur_number int4 not null default 0, - target_ident varchar(191) NOT NULL DEFAULT ''::character varying, - target_note varchar(191) NOT NULL DEFAULT ''::character varying, - first_trigger_time int8, - trigger_time int8 NOT NULL, - trigger_value varchar(255) NOT NULL, - tags varchar(1024) NOT NULL DEFAULT ''::character varying, - rule_prod varchar(255) NOT NULL DEFAULT ''::character varying, - rule_algo varchar(255) NOT NULL DEFAULT ''::character varying, - CONSTRAINT alert_cur_event_pk PRIMARY KEY (id) -); -CREATE INDEX alert_cur_event_hash_idx ON alert_cur_event USING btree (hash); -CREATE INDEX alert_cur_event_notify_repeat_next_idx ON alert_cur_event USING btree (notify_repeat_next); -CREATE INDEX alert_cur_event_rule_id_idx ON alert_cur_event USING btree (rule_id); -CREATE INDEX alert_cur_event_trigger_time_idx ON alert_cur_event USING btree (trigger_time, group_id); -COMMENT ON COLUMN alert_cur_event.id IS 'use alert_his_event.id'; -COMMENT ON COLUMN alert_cur_event.group_id IS 'busi group id of rule'; -COMMENT ON COLUMN alert_cur_event.group_name IS 'busi group name'; -COMMENT ON COLUMN alert_cur_event.hash IS 'rule_id + vector_pk'; -COMMENT ON COLUMN alert_cur_event.rule_note IS 'alert rule note'; -COMMENT ON COLUMN alert_cur_event.severity IS '1:Emergency 2:Warning 3:Notice'; -COMMENT ON COLUMN alert_cur_event.prom_for_duration IS 'prometheus for, unit:s'; -COMMENT ON COLUMN alert_cur_event.prom_ql IS 'promql'; -COMMENT ON COLUMN alert_cur_event.prom_eval_interval IS 'evaluate interval'; -COMMENT ON COLUMN alert_cur_event.callbacks IS 'split by space: http://a.com/api/x http://a.com/api/y'; -COMMENT ON COLUMN alert_cur_event.notify_recovered IS 'whether notify when recovery'; -COMMENT ON COLUMN alert_cur_event.notify_channels IS 'split by space: sms voice email dingtalk wecom'; -COMMENT ON COLUMN alert_cur_event.notify_groups IS 'split by space: 233 43'; -COMMENT ON COLUMN alert_cur_event.notify_repeat_next IS 'next timestamp to notify, get repeat settings from rule'; -COMMENT ON COLUMN alert_cur_event.target_ident IS 'target ident, also in tags'; -COMMENT ON COLUMN alert_cur_event.target_note IS 'target note'; -COMMENT ON COLUMN alert_cur_event.tags IS 'merge data_tags rule_tags, split by ,,'; - -CREATE TABLE alert_his_event ( - id bigserial NOT NULL, - is_recovered int2 NOT NULL, - cate varchar(128) not null default '' , - "cluster" varchar(128) NOT NULL, - group_id int8 NOT NULL, - group_name varchar(255) NOT NULL DEFAULT ''::character varying, - hash varchar(64) NOT NULL, - rule_id int8 NOT NULL, - rule_name varchar(255) NOT NULL, - rule_note varchar(2048) NOT NULL DEFAULT 'alert rule note'::character varying, - severity int2 NOT NULL, - prom_for_duration int4 NOT NULL, - prom_ql varchar(8192) NOT NULL, - prom_eval_interval int4 NOT NULL, - callbacks varchar(255) NOT NULL DEFAULT ''::character varying, - runbook_url varchar(255) NULL, - notify_recovered int2 NOT NULL, - notify_channels varchar(255) NOT NULL DEFAULT ''::character varying, - notify_groups varchar(255) NOT NULL DEFAULT ''::character varying, - notify_cur_number int4 not null default 0, - target_ident varchar(191) NOT NULL DEFAULT ''::character varying, - target_note varchar(191) NOT NULL DEFAULT ''::character varying, - first_trigger_time int8, - trigger_time int8 NOT NULL, - trigger_value varchar(255) NOT NULL, - recover_time int8 NOT NULL DEFAULT 0, - last_eval_time int8 NOT NULL DEFAULT 0, - tags varchar(1024) NOT NULL DEFAULT ''::character varying, - rule_prod varchar(255) NOT NULL DEFAULT ''::character varying, - rule_algo varchar(255) NOT NULL DEFAULT ''::character varying, - CONSTRAINT alert_his_event_pk PRIMARY KEY (id) -); -CREATE INDEX alert_his_event_hash_idx ON alert_his_event USING btree (hash); -CREATE INDEX alert_his_event_rule_id_idx ON alert_his_event USING btree (rule_id); -CREATE INDEX alert_his_event_trigger_time_idx ON alert_his_event USING btree (trigger_time, group_id); - -COMMENT ON COLUMN alert_his_event.group_id IS 'busi group id of rule'; -COMMENT ON COLUMN alert_his_event.group_name IS 'busi group name'; -COMMENT ON COLUMN alert_his_event.hash IS 'rule_id + vector_pk'; -COMMENT ON COLUMN alert_his_event.rule_note IS 'alert rule note'; -COMMENT ON COLUMN alert_his_event.severity IS '0:Emergency 1:Warning 2:Notice'; -COMMENT ON COLUMN alert_his_event.prom_for_duration IS 'prometheus for, unit:s'; -COMMENT ON COLUMN alert_his_event.prom_ql IS 'promql'; -COMMENT ON COLUMN alert_his_event.prom_eval_interval IS 'evaluate interval'; -COMMENT ON COLUMN alert_his_event.callbacks IS 'split by space: http://a.com/api/x http://a.com/api/y'; -COMMENT ON COLUMN alert_his_event.notify_recovered IS 'whether notify when recovery'; -COMMENT ON COLUMN alert_his_event.notify_channels IS 'split by space: sms voice email dingtalk wecom'; -COMMENT ON COLUMN alert_his_event.notify_groups IS 'split by space: 233 43'; -COMMENT ON COLUMN alert_his_event.target_ident IS 'target ident, also in tags'; -COMMENT ON COLUMN alert_his_event.target_note IS 'target note'; -COMMENT ON COLUMN alert_his_event.last_eval_time IS 'for time filter'; -COMMENT ON COLUMN alert_his_event.tags IS 'merge data_tags rule_tags, split by ,,'; - -CREATE TABLE task_tpl -( - id serial, - group_id int not null , - title varchar(255) not null default '', - account varchar(64) not null, - batch int not null default 0, - tolerance int not null default 0, - timeout int not null default 0, - pause varchar(255) not null default '', - script text not null, - args varchar(512) not null default '', - tags varchar(255) not null default '' , - create_at bigint not null default 0, - create_by varchar(64) not null default '', - update_at bigint not null default 0, - update_by varchar(64) not null default '' -) ; -ALTER TABLE task_tpl ADD CONSTRAINT task_tpl_pk PRIMARY KEY (id); -CREATE INDEX task_tpl_group_id_idx ON task_tpl (group_id); - -COMMENT ON COLUMN task_tpl.group_id IS 'busi group id'; -COMMENT ON COLUMN task_tpl.tags IS 'split by space'; - - -CREATE TABLE task_tpl_host -( - ii serial, - id int not null , - host varchar(128) not null -) ; -ALTER TABLE task_tpl_host ADD CONSTRAINT task_tpl_host_pk PRIMARY KEY (id); -CREATE INDEX task_tpl_host_id_idx ON task_tpl_host (id,host); - -COMMENT ON COLUMN task_tpl_host.id IS 'task tpl id'; -COMMENT ON COLUMN task_tpl_host.host IS 'ip or hostname'; - -CREATE TABLE task_record -( - id bigserial, - group_id bigint not null , - ibex_address varchar(128) not null, - ibex_auth_user varchar(128) not null default '', - ibex_auth_pass varchar(128) not null default '', - title varchar(255) not null default '', - account varchar(64) not null, - batch int not null default 0, - tolerance int not null default 0, - timeout int not null default 0, - pause varchar(255) not null default '', - script text not null, - args varchar(512) not null default '', - create_at bigint not null default 0, - create_by varchar(64) not null default '' -) ; -ALTER TABLE task_record ADD CONSTRAINT task_record_pk PRIMARY KEY (id); -CREATE INDEX task_record_create_at_idx ON task_record (create_at,group_id); -CREATE INDEX task_record_create_by_idx ON task_record (create_by); - -COMMENT ON COLUMN task_record.id IS 'ibex task id'; -COMMENT ON COLUMN task_record.group_id IS 'busi group id'; - -CREATE TABLE alerting_engines -( - id bigserial NOT NULL, - instance varchar(128) not null default '', - cluster varchar(128) not null default '', - clock bigint not null -) ; -ALTER TABLE alerting_engines ADD CONSTRAINT alerting_engines_pk PRIMARY KEY (id); -COMMENT ON COLUMN alerting_engines.instance IS 'instance identification, e.g. 10.9.0.9:9090'; -COMMENT ON COLUMN alerting_engines.cluster IS 'target reader cluster'; diff --git a/docker/mysqletc/my.cnf b/docker/mysqletc/my.cnf deleted file mode 100644 index 0ac9682159dcb5934dde40f0d0e0bde74972a8c2..0000000000000000000000000000000000000000 --- a/docker/mysqletc/my.cnf +++ /dev/null @@ -1,5 +0,0 @@ -[mysqld] -pid-file = /var/run/mysqld/mysqld.pid -socket = /var/run/mysqld/mysqld.sock -datadir = /var/lib/mysql -bind-address = 0.0.0.0 \ No newline at end of file diff --git a/docker/n9eetc/alerts/kafka_by_exporter.json b/docker/n9eetc/alerts/kafka_by_exporter.json deleted file mode 100644 index 1a52b4188ffc1c495dcdbf8a0be9a2924f202b6e..0000000000000000000000000000000000000000 --- a/docker/n9eetc/alerts/kafka_by_exporter.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "name": "数据有丢失风险-同步副本数小于3", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "sum(kafka_topic_partition_in_sync_replica) by (topic) < 3", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "消费能力不足-积压消息数超过50条", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "sum(kafka_topic_partition_current_offset{instance=\"$instance\"}) by (topic) - sum(kafka_consumergroup_current_offset{instance=\"$instance\"}) by (topic) ", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/elasticsearch_by_exporter.json b/docker/n9eetc/dashboards/elasticsearch_by_exporter.json deleted file mode 100644 index b2875944eb09b80416683469ef51d17149581630..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/elasticsearch_by_exporter.json +++ /dev/null @@ -1,431 +0,0 @@ -[ - { - "name": "ElasticSearch - 模板", - "tags": "Prometheus ElasticSearch ES", - "configs": "{\"var\":[{\"name\":\"cluster\",\"definition\":\"label_values(elasticsearch_indices_docs,cluster)\",\"options\":[\"elasticsearch-cluster\"]},{\"name\":\"instance\",\"definition\":\"label_values(elasticsearch_indices_docs{cluster=\\\"$cluster\\\", name!=\\\"\\\"},instance)\",\"options\":[\"10.206.0.7:9200\"]},{\"name\":\"name\",\"definition\":\"label_values(elasticsearch_indices_docs{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\", name!=\\\"\\\"},name)\",\"options\":[\"node-2\",\"node-1\",\"node-3\"],\"multi\":true,\"allOption\":true}]}", - "chart_groups": [ - { - "name": "KPI", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_number_of_nodes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Data Nodes\",\"description\":\"集群数据节点数量\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":6,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_number_of_nodes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Nodes\",\"description\":\"集群节点数量\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":3,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum (elasticsearch_process_cpu_percent{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"} ) / count (elasticsearch_process_cpu_percent{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"} )\"}],\"name\":\"CPU usage Avg\",\"description\":\"平均CPU使用率\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"special\":80,\"from\":80},\"result\":{\"color\":\"#f90606\"}},{\"type\":\"range\",\"match\":{\"special\":70,\"from\":70},\"result\":{\"color\":\"#f5ac0f\"}},{\"type\":\"range\",\"match\":{\"to\":70},\"result\":{\"color\":\"#21c00c\"}}],\"standardOptions\":{\"util\":\"percent\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":9,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum (elasticsearch_jvm_memory_used_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}) / sum (elasticsearch_jvm_memory_max_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}) * 100\"}],\"name\":\"JVM memory used Avg\",\"description\":\"平均JVM内存使用率\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"special\":80,\"from\":80},\"result\":{\"color\":\"#f12c09\"}},{\"type\":\"range\",\"match\":{\"from\":70,\"to\":80},\"result\":{\"color\":\"#fbca18\"}},{\"type\":\"range\",\"match\":{\"to\":70},\"result\":{\"color\":\"#21c00c\"}}],\"standardOptions\":{\"util\":\"percent\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":12,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum (elasticsearch_process_open_files_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"})\"}],\"name\":\"Open file descriptors\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":15,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(elasticsearch_breakers_tripped{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"})\"}],\"name\":\"Tripped for breakers\",\"description\":\"集群断路器阻断总数\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"result\":{\"text\":\"\",\"color\":\"#21c00c\"},\"match\":{\"special\":0}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":18,\"y\":0,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_number_of_pending_tasks{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Pending tasks\",\"description\":\"等待执行的集群变更任务数量\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":5},\"result\":{\"color\":\"#f24207\"}},{\"type\":\"range\",\"match\":{\"from\":1,\"to\":5},\"result\":{\"color\":\"#f9a006\"}},{\"type\":\"range\",\"match\":{\"to\":1},\"result\":{\"color\":\"#21c00c\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":21,\"y\":0,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_status{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",color=\\\"red\\\"}==1 or (elasticsearch_cluster_health_status{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",color=\\\"green\\\"}==1)+4 or (elasticsearch_cluster_health_status{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",color=\\\"yellow\\\"}==1)+2\"}],\"name\":\"Cluster health\",\"description\":\"绿色:健康,黄色:存在副本分片异常,红色:存在主分片异常\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"result\":{\"text\":\"N/A\"},\"match\":{}},{\"type\":\"special\",\"match\":{\"special\":5},\"result\":{\"text\":\"Green\",\"color\":\"#21c00c\"}},{\"type\":\"special\",\"match\":{\"special\":3},\"result\":{\"text\":\"Yellow\",\"color\":\"#f9e406\"}},{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"text\":\"Red\",\"color\":\"#f43606\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":0,\"y\":0,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "Shards", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_active_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Active shards\",\"description\":\"活跃分片数\\nAggregate total of all shards across all indices, which includes replica shards\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_active_primary_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Active primary shards\",\"description\":\"活跃主分片数\\nThe number of primary shards in your cluster. This is an aggregate total across all indices.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":4,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_initializing_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Initializing shards\",\"description\":\"创建中分片数量\\nCount of shards that are being freshly created\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":8,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_relocating_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Relocating shards\",\"description\":\"迁移中分片数量\\nThe number of shards that are currently moving from one node to another node.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_unassigned_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Unassigned shards\",\"description\":\"未分配的分片数量\\nThe number of shards that exist in the cluster state, but cannot be found in the cluster itself\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_delayed_unassigned_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Delayed shards\",\"description\":\"暂缓分配的分片数量,当节点丢失,会发生选主和数据拷贝。为了较少网络抖动等原因导致的重分配情况,配置delay参数,该值为等待delay到期将被重分配的分片数量\\nShards delayed to reduce reallocation overhead\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":0,\"i\":\"5\"}}", - "weight": 0 - } - ] - }, - { - "name": "JVM Garbage Collection", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_jvm_gc_collection_seconds_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}} - {{gc}}\"}],\"name\":\"GC count\",\"description\":\"GC运行次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_jvm_gc_collection_seconds_sum{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}} - {{gc}}\"}],\"name\":\"GC time\",\"description\":\"GC运行耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Translog", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_translog_operations{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Total translog operations\",\"description\":\"translog大小(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_translog_size_in_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Total translog size in bytes\",\"description\":\"translog大小(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Breakers", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_breakers_tripped{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{breaker}}\"}],\"name\":\"Tripped for breakers\",\"description\":\"节点断路器阻断总数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_breakers_estimated_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{breaker}}\"},{\"expr\":\"elasticsearch_breakers_limit_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"{{name}}: limit for {{breaker}}\"}],\"name\":\"Estimated size in bytes of breaker\",\"description\":\"预估内存大小和限制内存大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Cpu and Memory", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_os_load1{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"load1: {{name}}\"},{\"expr\":\"elasticsearch_os_load5{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"load5: {{name}}\"},{\"expr\":\"elasticsearch_os_load15{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"C\",\"legend\":\"load15: {{name}}\"}],\"name\":\"Load average\",\"description\":\"1m/5m/15m系统负载\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_process_cpu_percent{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"CPU usage\",\"description\":\"进程CPU占比\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_jvm_memory_used_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}} used: {{area}}\"},{\"expr\":\"elasticsearch_jvm_memory_max_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"{{name}} max: {{area}}\"},{\"expr\":\"elasticsearch_jvm_memory_pool_peak_used_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"C\",\"legend\":\"{{name}} peak used pool: {{pool}}\"}],\"name\":\"JVM memory usage\",\"description\":\"进程内存占用/限制/峰值(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_jvm_memory_committed_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}} committed: {{area}}\"},{\"expr\":\"elasticsearch_jvm_memory_max_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"{{name}} max: {{area}}\"}],\"name\":\"JVM memory committed\",\"description\":\"JVM申请/限制内存(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Disk and Network", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"1-(elasticsearch_filesystem_data_available_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}/elasticsearch_filesystem_data_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"})\",\"legend\":\"{{name}}: {{path}}\"}],\"name\":\"Disk usage\",\"description\":\"磁盘使用率\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_transport_tx_size_bytes_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: sent\"},{\"expr\":\"-irate(elasticsearch_transport_rx_size_bytes_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{name}}: received\"}],\"name\":\"Network usage\",\"description\":\"网络流量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Documents", - "weight": 7, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_docs{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Documents count on node\",\"description\":\"节点文档总数,不包含已删除文档和子文档以及刚索引的文档\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_indexing_index_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents indexed rate\",\"description\":\"平均每秒索引文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_docs_deleted{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents deleted rate\",\"description\":\"平均每秒删除文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_merges_docs_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents merged rate\",\"description\":\"平均每秒合并文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_merges_total_size_bytes_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents merged bytes\",\"description\":\"平均每秒合并文档大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "Times", - "weight": 8, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_search_query_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Query time\",\"description\":\"查询操作耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_indexing_index_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Indexing time\",\"description\":\"索引操作耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_merges_total_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Merging time\",\"description\":\"合并操作耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_store_throttle_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Throttle time for index store\",\"description\":\"索引存储限制耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Total Operations states", - "weight": 9, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_indexing_index_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: indexing\"},{\"expr\":\"irate(elasticsearch_indices_search_query_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{name}}: query\"},{\"expr\":\"irate(elasticsearch_indices_search_fetch_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"C\",\"legend\":\"{{name}}: fetch\"},{\"expr\":\"irate(elasticsearch_indices_merges_total_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"D\",\"legend\":\"{{name}}: merges\"},{\"expr\":\"irate(elasticsearch_indices_refresh_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"E\",\"legend\":\"{{name}}: refresh\"},{\"expr\":\"irate(elasticsearch_indices_flush_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"F\",\"legend\":\"{{name}}: flush\"},{\"expr\":\"irate(elasticsearch_indices_get_exists_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"G\",\"legend\":\"{{name}}: get_exists\"},{\"expr\":\"irate(elasticsearch_indices_get_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"H\",\"legend\":\"{{name}}: get_time\"},{\"expr\":\"irate(elasticsearch_indices_get_missing_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"I\",\"legend\":\"{{name}}: get_missing\"},{\"expr\":\"irate(elasticsearch_indices_indexing_delete_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"K\",\"legend\":\"{{name}}: indexing_delete\"},{\"expr\":\"irate(elasticsearch_indices_get_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"L\",\"legend\":\"{{name}}: get\"}],\"name\":\"Total Operations time\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_indexing_index_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: indexing\"},{\"expr\":\"rate(elasticsearch_indices_search_query_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{name}}: query\"},{\"expr\":\"rate(elasticsearch_indices_search_fetch_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"C\",\"legend\":\"{{name}}: fetch\"},{\"expr\":\"rate(elasticsearch_indices_merges_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"D\",\"legend\":\"{{name}}: merges\"},{\"expr\":\"rate(elasticsearch_indices_refresh_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"E\",\"legend\":\"{{name}}: refresh\"},{\"expr\":\"rate(elasticsearch_indices_flush_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"F\",\"legend\":\"{{name}}: flush\"},{\"expr\":\"rate(elasticsearch_indices_get_exists_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"G\",\"legend\":\"{{name}}: get_exists\"},{\"expr\":\"rate(elasticsearch_indices_get_missing_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"H\",\"legend\":\"{{name}}: get_missing\"},{\"expr\":\"rate(elasticsearch_indices_get_tota{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"I\",\"legend\":\"{{name}}: get\"},{\"expr\":\"rate(elasticsearch_indices_indexing_delete_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"K\",\"legend\":\"{{name}}: indexing_delete\"}],\"name\":\"Total Operations rate\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Thread Pool", - "weight": 10, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_thread_pool_rejected_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool operations rejected\",\"description\":\"线程池reject次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_thread_pool_active_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool threads active\",\"description\":\"活跃线程数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_thread_pool_queue_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool threads queued\",\"description\":\"排队等待线程任务数量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_thread_pool_completed_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool operations completed\",\"description\":\"线程池complete次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Caches", - "weight": 11, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_fielddata_memory_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Field data memory size\",\"description\":\"fielddata cache内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_fielddata_evictions{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Field data evictions\",\"description\":\"fielddata cache平均每秒内存剔除次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_query_cache_memory_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Query cache size\",\"description\":\"query cache内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_query_cache_evictions{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Query cache evictions\",\"description\":\"query cache平均每秒内存剔除次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_filter_cache_evictions{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Evictions from filter cache\",\"description\":\"老版本的filter cache内存剔除次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "Segments", - "weight": 12, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segments_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Count of index segments\",\"description\":\"segment个数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segments_memory_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Current memory size of segments in bytes\",\"description\":\"segment内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Count of documents and Total size", - "weight": 13, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_docs_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Count of documents with only primary shards\",\"description\":\"主分片文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"stack\":\"off\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_store_size_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Total size of stored index data in bytes with only primary shards on all nodes\",\"description\":\"主分片索引容量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_store_size_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Total size of stored index data in bytes with all shards on all nodes\",\"description\":\"所有分片索引容量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":4,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Index writer", - "weight": 14, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_index_writer_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Index writer with only primary shards on all nodes in bytes\",\"description\":\"主分片索引写入数据量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_index_writer_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Index writer with all shards on all nodes in bytes\",\"description\":\"所有分片索引写入数据量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Segments", - "weight": 15, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_count_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Segments with only primary shards on all nodes\",\"description\":\"主分片segment数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_count_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Segments with all shards on all nodes\",\"description\":\"所有分片segment总数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of segments with only primary shards on all nodes in bytes\",\"description\":\"主分片segment容量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":4,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of segments with all shards on all nodes in bytes\",\"description\":\"所有分片segment容量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":6,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Doc values", - "weight": 16, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_doc_values_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Doc values with only primary shards on all nodes in bytes\",\"description\":\"主分片doc value内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_doc_values_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Doc values with all shards on all nodes in bytes\",\"description\":\"所有分片doc value内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Fields", - "weight": 17, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fields_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fields with only primary shards on all nodes in bytes\",\"description\":\"分片field内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fields_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fields with all shards on all nodes in bytes\",\"description\":\"所有分片field内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Fixed bit", - "weight": 18, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fixed_bit_set_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fixed bit with only primary shards on all nodes in bytes\",\"description\":\"主分片fixed bit set内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fixed_bit_set_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fixed bit with all shards on all nodes in bytes\",\"description\":\"所有分片fixed bit set内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Norms", - "weight": 19, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_norms_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of norms with only primary shards on all nodes in bytes\",\"description\":\"主分片normalization factor内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_norms_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of norms with all shards on all nodes in bytes\",\"description\":\"所有分片normalization factor内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Points", - "weight": 20, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_points_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of points with only primary shards on all nodes in bytes\",\"description\":\"主分片point内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_points_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of points with all shards on all nodes in bytes\",\"description\":\"所有分片point内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Terms", - "weight": 21, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_terms_memory_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of terms with only primary shards on all nodes in bytes\",\"description\":\"主分片term内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_terms_memory_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Number of terms with all shards on all nodes in bytes\",\"description\":\"所有分片term内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Terms", - "weight": 22, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_version_map_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of version map with only primary shards on all nodes in bytes\",\"description\":\"所有分片version map内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_version_map_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of version map with all shards on all nodes in bytes\",\"description\":\"所有分片version map内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/http_response_by_categraf.json b/docker/n9eetc/dashboards/http_response_by_categraf.json deleted file mode 100644 index a77b0fb5ca72bee94711a82e276e978c42122a05..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/http_response_by_categraf.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "HTTP探测", - "tags": "", - "configs": "", - "chart_groups": [ - { - "name": "Default chart group", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"max(http_response_result_code) by (target)\",\"legend\":\"UP?\"},{\"expr\":\"max(http_response_response_code) by (target)\",\"refId\":\"B\",\"legend\":\"status code\"},{\"expr\":\"max(http_response_response_time) by (target)\",\"refId\":\"C\",\"legend\":\"latency(s)\"},{\"expr\":\"max(http_response_cert_expire_timestamp) by (target) - time()\",\"refId\":\"D\",\"legend\":\"cert expire\"}],\"name\":\"URL Details\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelValuesToRows\",\"aggrDimension\":\"target\"},\"options\":{\"valueMappings\":[],\"standardOptions\":{}},\"overrides\":[{\"properties\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"UP\",\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"special\":1,\"from\":1},\"result\":{\"text\":\"DOWN\",\"color\":\"#e90f0f\"}}],\"standardOptions\":{}},\"matcher\":{\"value\":\"A\"}},{\"type\":\"special\",\"matcher\":{\"value\":\"D\"},\"properties\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":4,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } -] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/jmx_by_exporter.json b/docker/n9eetc/dashboards/jmx_by_exporter.json deleted file mode 100644 index 79ca038930922669bc6dd3e9fe3ac5617403c857..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/jmx_by_exporter.json +++ /dev/null @@ -1,125 +0,0 @@ -[ - { - "name": "JMX - 模板", - "tags": "Prometheus JMX", - "configs": "{\"var\":[{\"name\":\"job\",\"definition\":\"jmx_exporter\"},{\"name\":\"instance\",\"definition\":\"label_values(jmx_scrape_error,instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"up{job=\\\"$job\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Status\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"text\":\"UP\",\"color\":\"#1eac02\"}},{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"DOWN\",\"color\":\"#f00a0a\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"time() - process_start_time_seconds{job=\\\"$job\\\",instance=\\\"$instance\\\"}\"}],\"name\":\"Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"os_available_processors{job=\\\"$job\\\",instance=\\\"$instance\\\"}\"}],\"name\":\"Available CPUs\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"os_open_file_descriptor_count{job=\\\"$job\\\",instance=\\\"$instance\\\"}\"}],\"name\":\"Open file descriptors\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "JVM Memory", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_bytes_used{area=\\\"heap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_bytes_max{area=\\\"heap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Max\"}],\"name\":\"JVM Memory(heap)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_bytes_used{area=\\\"nonheap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_bytes_max{area=\\\"nonheap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Max\"}],\"name\":\"JVM Memory(nonheap)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Memory Pool", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"CodeHeap 'non-nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"CodeHeap 'non-nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"CodeHeap 'non-nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"CodeHeap 'non-nmethods'\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"CodeHeap 'profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"CodeHeap 'profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"CodeHeap 'profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"CodeHeap 'profiled nmethods'\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"CodeHeap 'non-profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"CodeHeap 'non-profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"CodeHeap 'non-profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"CodeHeap 'non-profiled nmethods'\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"G1 Eden Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"G1 Eden Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"G1 Eden Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"G1 Eden Space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"Compressed Class Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"Compressed Class Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"Compressed Class Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"Compressed Class Space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"G1 Survivor Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"G1 Survivor Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"G1 Survivor Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"G1 Survivor Space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"G1 Old Gen\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"G1 Old Gen\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"G1 Old Gen\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"G1 Old Gen\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"Metaspace\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"Metaspace\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"Metaspace\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"Metaspace\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":2,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "GC", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"increase(jvm_gc_collection_seconds_sum{job=\\\"$job\\\",instance=~\\\"$instance\\\"}[1m])\"}],\"name\":\"过去一分钟GC耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"increase(jvm_gc_collection_seconds_count{job=\\\"$job\\\",instance=\\\"$instance\\\"}[1m])\",\"legend\":\"\"}],\"name\":\"过去一分钟GC次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"increase(jvm_gc_collection_seconds_sum{job=\\\"$job\\\",instance=\\\"$instance\\\"}[1m])/increase(jvm_gc_collection_seconds_count{job=\\\"$job\\\",instance=\\\"$instance\\\"}[1m])\",\"legend\":\"\"}],\"name\":\"过去一分钟每次GC平均耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"bars\",\"stack\":\"off\",\"fillOpacity\":1},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Threads and Class loading", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_threads_current{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"current\"},{\"expr\":\"jvm_threads_daemon{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"daemon\"},{\"expr\":\"jvm_threads_deadlocked{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"deadlocked\"}],\"name\":\"Threads\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_classes_loaded{job=\\\"$job\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Class loading\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Physical memory", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"os_total_physical_memory_bytes{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Total physical memory\"},{\"expr\":\"os_committed_virtual_memory_bytes{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Committed virtual memory\"},{\"expr\":\"os_free_physical_memory_bytes{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Free physical memory\"}],\"name\":\"Physical memory\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/kafka_by_exporter.json b/docker/n9eetc/dashboards/kafka_by_exporter.json deleted file mode 100644 index 47f4cc1c80abb342294d67ebb2d41d28f15d0268..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/kafka_by_exporter.json +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "name": "Kafka - 模板", - "tags": "Kafka Prometheus ", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(kafka_brokers, instance)\"},{\"name\":\"job\",\"definition\":\"label_values(kafka_brokers, job)\"}]}", - "chart_groups": [ - { - "name": "overview", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"count(count by (topic) (kafka_topic_partitions))\"}],\"name\":\"topics\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":8,\"x\":8,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"kafka_brokers\"}],\"name\":\"brokers\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":8,\"x\":0,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(kafka_topic_partitions)\"}],\"name\":\"partitions\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "throughput", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(kafka_topic_partition_current_offset{instance=\\\"$instance\\\"}[1m])) by (topic)\"}],\"name\":\"Message in per second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(kafka_consumer_lag_millis{instance=\\\"$instance\\\"}) by (consumergroup, topic) \",\"legend\":\"{{consumergroup}} (topic: {{topic}})\"}],\"name\":\"Latency by Consumer Group\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"humantimeMilliseconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(kafka_consumergroup_current_offset{instance=\\\"$instance\\\"}[1m])) by (topic)\"}],\"name\":\"Message consume per second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(kafka_topic_partition_current_offset{instance=\\\"$instance\\\"}) by (topic) - sum(kafka_consumergroup_current_offset{instance=\\\"$instance\\\"}) by (topic) \",\"legend\":\"{{consumergroup}} (topic: {{topic}})\"}],\"name\":\"Lag by Consumer Group\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "patition/replicate", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"kafka_topic_partitions{instance=\\\"$instance\\\"}\",\"legend\":\"{{topic}}\"}],\"name\":\"Partitions per Topic\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"seriesToRows\"},\"options\":{\"standardOptions\":{}},\"overrides\":[{}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"kafka_topic_partition_under_replicated_partition\",\"legend\":\"{{topic}}-{{partition}}\"}],\"name\":\"Under Replicated\",\"description\":\"副本不同步预案\\n1. Restart the Zookeeper leader.\\n2. Restart the broker\\\\brokers that are not replicating some of the partitions.\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"seriesToRows\"},\"options\":{\"standardOptions\":{}},\"overrides\":[{}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/linux_by_categraf.json b/docker/n9eetc/dashboards/linux_by_categraf.json deleted file mode 100644 index 1f0cacc2907a64d6bb45ae423a2919dceb4dbf77..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/linux_by_categraf.json +++ /dev/null @@ -1,1523 +0,0 @@ -{ - "name": "Linux Host", - "tags": "", - "configs": { - "var": [ - { - "name": "ident", - "definition": "label_values(system_load1,ident)" - } - ], - "links": [ - { - "title": "n9e", - "url": "https://n9e.github.io/", - "targetBlank": true - }, - { - "title": "author", - "url": "http://flashcat.cloud/", - "targetBlank": true - } - ], - "version": "2.0.0", - "panels": [ - { - "id": "e5d14dd7-4417-42bd-b7ba-560f34d299a2", - "type": "row", - "name": "整体概况", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 0, - "i": "e5d14dd7-4417-42bd-b7ba-560f34d299a2" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "count(system_load1)" - } - ], - "name": "监控机器数", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 50 - } - }, - "options": { - "standardOptions": {} - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 0, - "y": 1, - "i": "41f37540-e695-492a-9d2f-24bfd2d36805" - }, - "id": "41f37540-e695-492a-9d2f-24bfd2d36805" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (mem_used_percent))" - } - ], - "name": "内存率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 9, - "x": 3, - "y": 1, - "i": "585bfc50-7c92-42b1-88ee-5b725b640418" - }, - "id": "585bfc50-7c92-42b1-88ee-5b725b640418" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (100-cpu_usage_idle{cpu=\"cpu-total\"}))" - } - ], - "name": "cpu使用率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 12, - "y": 1, - "i": "60b1e833-3f03-45bb-9385-a3825904a0ac" - }, - "id": "60b1e833-3f03-45bb-9385-a3825904a0ac" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (disk_used_percent{path!~\"/var.*\"}))", - "legend": "{{ident}}-{{path}}" - } - ], - "name": "磁盘分区使用率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 0, - "y": 2, - "i": "69351db9-e646-4e5d-925a-cba29823b00d" - }, - "id": "69351db9-e646-4e5d-925a-cba29823b00d" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (rate(diskio_io_time[1m])/10))", - "legend": "" - } - ], - "name": "设备io util top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 12, - "y": 2, - "i": "e3675ed9-6d3b-4a41-8d16-d6e82037dce3" - }, - "id": "e3675ed9-6d3b-4a41-8d16-d6e82037dce3" - }, - { - "id": "2b2de3d1-65c8-4c39-9bea-02b754e0d751", - "type": "row", - "name": "单机概况", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 5, - "i": "2b2de3d1-65c8-4c39-9bea-02b754e0d751" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "system_uptime{ident=\"$ident\"}" - } - ], - "name": "启动时长", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [], - "standardOptions": { - "util": "humantimeSeconds", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 0, - "y": 6, - "i": "deec579b-3090-4344-a9a6-c1455c4a8e50" - }, - "id": "deec579b-3090-4344-a9a6-c1455c4a8e50" - }, - { - "targets": [ - { - "refId": "A", - "expr": "100-cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}" - } - ], - "name": "CPU使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 6, - "y": 6, - "i": "7a7bd5db-d12e-49f0-92a8-15958e99ee54" - }, - "id": "7a7bd5db-d12e-49f0-92a8-15958e99ee54" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_used_percent{ident=\"$ident\"}" - } - ], - "name": "内存使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 12, - "y": 6, - "i": "8a814265-54ad-419c-8cb7-e1f84a242de0" - }, - "id": "8a814265-54ad-419c-8cb7-e1f84a242de0" - }, - { - "targets": [ - { - "refId": "A", - "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}/linux_sysctl_fs_file_max{ident=\"$ident\"}*100" - } - ], - "name": "FD使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 25 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 2 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 18, - "y": 6, - "i": "d7d11972-5c5b-4bc6-98f8-bbbe9f018896" - }, - "id": "d7d11972-5c5b-4bc6-98f8-bbbe9f018896" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_swap_total{ident=\"$ident\"}-mem_swap_free{ident=\"$ident\"}" - } - ], - "name": "SWAP使用", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 40 - } - }, - "options": { - "valueMappings": [], - "standardOptions": { - "util": "bytesIEC", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 21, - "y": 6, - "i": "209d3aba-5e02-4b8f-a364-65f20ba92a2c" - }, - "id": "209d3aba-5e02-4b8f-a364-65f20ba92a2c" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_used_percent{ident=\"$ident\"}", - "legend": "{{path}}" - } - ], - "name": "磁盘使用率", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 0, - "y": 7, - "i": "b3c5dd9d-e82a-4b15-8b23-c510e2bee152" - }, - "id": "b3c5dd9d-e82a-4b15-8b23-c510e2bee152" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_inodes_used{ident=\"$ident\"}/disk_inodes_total{ident=\"$ident\"}", - "legend": "{{path}}" - } - ], - "name": "inode使用率", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 8, - "y": 7, - "i": "0de74cd9-cc74-4a96-bcb2-05d3a8bde2ea" - }, - "id": "0de74cd9-cc74-4a96-bcb2-05d3a8bde2ea" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_io_time{ident=\"$ident\"}[1m])/10", - "legend": "{{name}}" - } - ], - "name": "io_util", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 16, - "y": 7, - "i": "59afa167-434d-496c-a3ef-ceff6db7c1f6" - }, - "id": "59afa167-434d-496c-a3ef-ceff6db7c1f6" - }, - { - "id": "aabb8263-1a9b-43fb-bee1-6c532f5012a3", - "type": "row", - "name": "系统指标", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 10, - "i": "aabb8263-1a9b-43fb-bee1-6c532f5012a3" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "processes_total{ident=\"$ident\"}" - } - ], - "name": "进程总数", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 2000, - "color": "#fa2a05" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 11, - "i": "1b4da538-29d4-4c58-b3f4-773fabb8616c" - }, - "id": "1b4da538-29d4-4c58-b3f4-773fabb8616c" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(kernel_context_switches{ident=\"$ident\"}[1m])", - "legend": "context_switches" - }, - { - "expr": "rate(kernel_interrupts{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "kernel_interrupts" - } - ], - "name": "上下文切换/中断", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 11, - "i": "aa7adae0-ae3b-4e28-a8ce-801c65961552" - }, - "id": "aa7adae0-ae3b-4e28-a8ce-801c65961552" - }, - { - "targets": [ - { - "refId": "A", - "expr": "kernel_entropy_avail{ident=\"$ident\"}", - "legend": "entropy_avail" - } - ], - "name": "熵池大小", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 100, - "color": "#f50505" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 11, - "i": "71e22f58-5b9a-4604-bca8-55bcef59b5fe" - }, - "id": "71e22f58-5b9a-4604-bca8-55bcef59b5fe" - }, - { - "id": "10f34f8f-f94d-4a28-9551-16e6667e3833", - "type": "row", - "name": "CPU", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 18, - "i": "10f34f8f-f94d-4a28-9551-16e6667e3833" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}", - "legend": "cpu_usage_idle" - } - ], - "name": "CPU空闲率", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 10, - "color": "#f20202" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 19, - "i": "1559d880-7e26-4e42-9427-4e55fb6f67be" - }, - "id": "1559d880-7e26-4e42-9427-4e55fb6f67be" - }, - { - "targets": [ - { - "refId": "A", - "expr": "cpu_usage_guest{ident=\"$ident\",cpu=\"cpu-total\"}", - "legend": "" - }, - { - "expr": "cpu_usage_iowait{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "B", - "legend": "" - }, - { - "expr": "cpu_usage_user{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "C" - }, - { - "expr": "cpu_usage_system{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "D" - }, - { - "expr": "cpu_usage_irq{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "E" - }, - { - "expr": "cpu_usage_softirq{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "F" - }, - { - "expr": "cpu_usage_nice{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "G" - }, - { - "expr": "cpu_usage_steal{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "H" - } - ], - "name": "CPU使用率详情", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 19, - "i": "043c26de-d19f-4fe8-a615-2b7c10ceb828" - }, - "id": "043c26de-d19f-4fe8-a615-2b7c10ceb828" - }, - { - "targets": [ - { - "refId": "A", - "expr": "system_load15{ident=\"$ident\"}" - }, - { - "expr": "system_load1{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "system_load5{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "CPU负载", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 19, - "i": "a420ce25-6968-47f8-8335-60cde70fd062" - }, - "id": "a420ce25-6968-47f8-8335-60cde70fd062" - }, - { - "id": "b7a3c99f-a796-4b76-89b5-cbddd566f91c", - "type": "row", - "name": "内存详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 26, - "i": "b7a3c99f-a796-4b76-89b5-cbddd566f91c" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_active{ident=\"$ident\"}" - }, - { - "expr": "mem_cached{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "mem_buffered{ident=\"$ident\"}", - "refId": "C" - }, - { - "expr": "mem_inactive{ident=\"$ident\"}", - "refId": "D" - }, - { - "expr": "mem_mapped{ident=\"$ident\"}", - "refId": "E" - }, - { - "expr": "mem_shared{ident=\"$ident\"}", - "refId": "F" - }, - { - "expr": "mem_swap_cached{ident=\"$ident\"}", - "refId": "G" - } - ], - "name": "用户态内存使用", - "description": "内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) ", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 12, - "x": 0, - "y": 27, - "i": "239aacdf-1982-428b-b240-57f4ce7f946d" - }, - "id": "239aacdf-1982-428b-b240-57f4ce7f946d" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_slab{ident=\"$ident\"}" - }, - { - "expr": "mem_sreclaimable{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "mem_sunreclaim{ident=\"$ident\"}", - "refId": "C" - }, - { - "expr": "mem_vmalloc_used{ident=\"$ident\"}", - "refId": "D" - }, - { - "expr": "mem_vmalloc_chunk{ident=\"$ident\"}", - "refId": "E" - } - ], - "name": "内核态内存使用", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 12, - "x": 12, - "y": 27, - "i": "00ed6e4d-c979-4938-a20e-56d42ca452cf" - }, - "id": "00ed6e4d-c979-4938-a20e-56d42ca452cf" - }, - { - "id": "842a8c48-0e93-40bf-8f28-1b2f837e5c19", - "type": "row", - "name": "磁盘详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 34, - "i": "842a8c48-0e93-40bf-8f28-1b2f837e5c19" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_free{ident=\"$ident\"}" - }, - { - "expr": "disk_total{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "disk_used{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "磁盘空间", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": null - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 35, - "i": "bc894871-1c03-4d12-91be-6867f394a8a6" - }, - "id": "bc894871-1c03-4d12-91be-6867f394a8a6" - }, - { - "targets": [ - { - "refId": "A", - "expr": "linux_sysctl_fs_file_max{ident=\"$ident\"}" - }, - { - "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}", - "refId": "B" - } - ], - "name": "fd使用", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 35, - "i": "d825671f-7dc5-46a2-89dc-4fff084a3ae0" - }, - "id": "d825671f-7dc5-46a2-89dc-4fff084a3ae0" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_inodes_total{ident=\"$ident\",path!~\"/var.*\"}", - "legend": "{{path}}-total" - }, - { - "expr": "disk_inodes_used{ident=\"$ident\",path!~\"/var.*\"}", - "refId": "B", - "legend": "{{path}}-used" - } - ], - "name": "inode", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 35, - "i": "d27b522f-9c70-42f2-9e31-fed3816fd675" - }, - "id": "d27b522f-9c70-42f2-9e31-fed3816fd675" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_reads{ident=\"$ident\"}[1m])", - "legend": "{{name}}-read" - }, - { - "expr": "rate(diskio_writes{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{name}}-writes" - } - ], - "name": "IOPS", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 37, - "i": "f645741e-c632-4685-b267-c7ad26b5c10e" - }, - "id": "f645741e-c632-4685-b267-c7ad26b5c10e" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_read_bytes{ident=\"$ident\"}[1m])", - "legend": "{{name}}-read" - }, - { - "expr": "rate(diskio_write_bytes{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{name}}-writes" - } - ], - "name": "IO吞吐量", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 37, - "i": "bbd1ebda-99f6-419c-90a5-5f84973976dd" - }, - "id": "bbd1ebda-99f6-419c-90a5-5f84973976dd" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_write_time{ident=\"$ident\"}[1m])/rate(diskio_writes{ident=\"$ident\"}[1m])+rate(diskio_read_time{ident=\"$ident\"}[1m])/rate(diskio_reads{ident=\"$ident\"}[1m])", - "legend": "{{name}}" - } - ], - "name": "iowait", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 37, - "i": "d6b45598-54c6-4b36-a896-0a7529ac21f8" - }, - "id": "d6b45598-54c6-4b36-a896-0a7529ac21f8" - }, - { - "id": "307152d2-708c-4736-98cf-08b886cbf7f2", - "type": "row", - "name": "网络详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 44, - "i": "307152d2-708c-4736-98cf-08b886cbf7f2" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_bytes_recv{ident=\"$ident\"}[1m])*8", - "legend": "{{interface}}-recv" - }, - { - "expr": "rate(net_bytes_sent{ident=\"$ident\"}[1m])*8", - "refId": "B", - "legend": "{{interface}}-sent" - } - ], - "name": "网络流量", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 0, - "y": 45, - "i": "f2ee5d32-737c-4095-b6b7-b15b778ffdb9" - }, - "id": "f2ee5d32-737c-4095-b6b7-b15b778ffdb9" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_packets_recv{ident=\"$ident\"}[1m])", - "legend": "{{interface}}-recv" - }, - { - "expr": "rate(net_packets_sent{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{interface}}-sent" - } - ], - "name": "packets", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 6, - "y": 45, - "i": "9113323a-98f5-4bff-a8ce-3b459e7e2190" - }, - "id": "9113323a-98f5-4bff-a8ce-3b459e7e2190" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_err_in{ident=\"$ident\"}[1m])", - "legend": "{{interface}}-in" - }, - { - "expr": "rate(net_err_out{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{interface}}-out" - } - ], - "name": "error", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 12, - "y": 45, - "i": "9634c41c-e124-4d7f-9406-0f86753e8d70" - }, - "id": "9634c41c-e124-4d7f-9406-0f86753e8d70" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_drop_in{ident=\"$ident\"}[1m])", - "legend": "{{interface}}-in" - }, - { - "expr": "rate(net_drop_out{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{interface}}-out" - } - ], - "name": "drop", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 18, - "y": 45, - "i": "4123f4c1-bf8e-400e-b267-8d7f6a92691a" - }, - "id": "4123f4c1-bf8e-400e-b267-8d7f6a92691a" - }, - { - "targets": [ - { - "refId": "A", - "expr": "netstat_tcp_established{ident=\"$ident\"}" - }, - { - "expr": "netstat_tcp_listen{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "netstat_tcp_time_wait{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "tcp", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 24, - "x": 0, - "y": 47, - "i": "cfb80689-de7b-47fb-9155-052b796dd7f5" - }, - "id": "cfb80689-de7b-47fb-9155-052b796dd7f5" - } - ] - } -} \ No newline at end of file diff --git a/docker/n9eetc/dashboards/linux_by_exporter.json b/docker/n9eetc/dashboards/linux_by_exporter.json deleted file mode 100644 index 738823c9110981d6457b65a3a5c2bffeb8300396..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/linux_by_exporter.json +++ /dev/null @@ -1,223 +0,0 @@ -[ - { - "name": "HOST - 模板", - "tags": "Prometheus Host", - "configs": "{\"var\":[{\"name\":\"node\",\"definition\":\"label_values(node_cpu_seconds_total, instance)\",\"selected\":\"$node\",\"options\":[\"tt-fc-es01.nj:12345\",\"tt-fc-es02.nj:12345\",\"tt-fc-dev01.nj:12345\",\"10.206.0.13:9100\"]}],\"links\":[{\"title\":\"n9e\",\"url\":\"https://n9e.gitee.io/\",\"targetBlank\":true},{\"title\":\"author\",\"url\":\"http://flashcat.cloud/\",\"targetBlank\":true}]}", - "chart_groups": [ - { - "name": "整体概况", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,100-(avg by (mode, instance)(rate(node_cpu_seconds_total{mode=\\\"idle\\\"}[1m])))*100)\",\"legend\":\"{{instance}}\"}],\"name\":\"cpu使用率 top10\",\"links\":[],\"description\":\"\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":9,\"x\":3,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - (node_memory_Cached_bytes + node_memory_Buffers_bytes))/node_memory_MemTotal_bytes*100)\",\"legend\":\"{{instance}}\"}],\"name\":\"内存使用率 top10\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,(node_filesystem_avail_bytes{device!~'rootfs', device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"} * 100) / node_filesystem_size_bytes{device!~'rootfs', device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"})\",\"legend\":\"{{instance}}-{{mountpoint}}\"}],\"name\":\"磁盘分区使用率 top10\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,rate(node_disk_io_time_seconds_total[5m]) * 100)\",\"legend\":\"{{instance}}-{{device}}\"}],\"name\":\"设备io util top10\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"count(node_boot_time_seconds)\"}],\"name\":\"监控机器数\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":40}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":0,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "单机概况", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"(node_memory_MemTotal_bytes{instance=\\\"$node\\\"} - node_memory_MemFree_bytes{instance=\\\"$node\\\"} - (node_memory_Cached_bytes{instance=\\\"$node\\\"} + node_memory_Buffers_bytes{instance=\\\"$node\\\"}))/node_memory_MemTotal_bytes{instance=\\\"$node\\\"}*100\"}],\"name\":\"内存使用率\",\"description\":\"如果内存使用率超过50%,则需要扩容或者升级配置了\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":25}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"from\":50,\"to\":100},\"result\":{\"color\":\"#e3170d\"}}],\"standardOptions\":{\"util\":\"percent\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(((count(count(node_cpu_seconds_total{instance=\\\"$node\\\"}) by (cpu))) - avg(sum by (mode)(rate(node_cpu_seconds_total{mode='idle',instance=\\\"$node\\\"}[1m])))) * 100) / count(count(node_cpu_seconds_total{instance=\\\"$node\\\"}) by (cpu))\"}],\"name\":\"CPU使用率\",\"description\":\"如果cpu使用率超过50%,可以通过top命令查看机器上是否有异常进程,如果没有异常进程,则说明服务需要扩容或者机器需要升级配置了\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":30}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"special\":50,\"from\":50,\"to\":100},\"result\":{\"color\":\"#b22222\"}}],\"standardOptions\":{\"util\":\"percent\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"max(100 - ((node_filesystem_avail_bytes{instance=\\\"$node\\\",} * 100) / node_filesystem_size_bytes{instance=\\\"$node\\\"}))\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"磁盘分区使用率最大值\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\",\"decimals\":1},\"thresholds\":{\"steps\":[{\"value\":90,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_time_seconds{instance=\\\"$node\\\"} - node_boot_time_seconds{instance=\\\"$node\\\"}\"}],\"name\":\"启动时长\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"title\":null,\"value\":20}},\"options\":{\"valueMappings\":[],\"standardOptions\":{\"util\":\"humantimeSeconds\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":21,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(node_memory_SwapTotal_bytes{instance=\\\"$node\\\"} - node_memory_SwapFree_bytes{instance=\\\"$node\\\"})\"}],\"name\":\"SWAP内存使用\",\"description\":\"swap使用过高,会影响系统io性能,如果内存够用但swap使用很高,可以调小swappiness的值\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":30}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"special\":50,\"from\":50,\"to\":80},\"result\":{\"color\":\"#fb9b2d\"}},{\"type\":\"range\",\"match\":{\"from\":80,\"to\":100000},\"result\":{\"color\":\"#d10000\"}}],\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":12,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_vmstat_oom_kill{instance=\\\"$node\\\"}[1m])\",\"legend\":\"OOM\"}],\"name\":\"OOM次数\",\"description\":\"大于0,说明有进程内存不够用了,需要考虑扩容或升级配置了\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{\"steps\":[{\"value\":1,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":1,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"max(rate(node_disk_io_time_seconds_total{instance=\\\"$node\\\"}[5m]) * 100)\",\"legend\":\"{{device}}\"}],\"name\":\"磁盘设备io util 最大值\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":1,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filefd_allocated{instance=\\\"$node\\\"}/node_filefd_maximum{instance=\\\"$node\\\"}*100\"}],\"name\":\"FD使用率\",\"description\":\"如果超过80%,建议把文件描述符的最大个数调大,或者扩容\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":25}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"special\":50,\"from\":50,\"to\":80},\"result\":{\"color\":\"#fb9b2d\"}},{\"type\":\"range\",\"match\":{\"from\":80,\"to\":100},\"result\":{\"color\":\"#d10000\"}}],\"standardOptions\":{\"util\":\"percent\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":15,\"y\":0,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"max(100 - ((node_filesystem_files_free{instance=\\\"$node\\\",mountpoint!~\\\"/var/lib/.*\\\",mountpoint!~\\\"/run/user.*\\\"} * 100) / node_filesystem_files{instance=\\\"$node\\\",mountpoint!~\\\"/var/lib/.*\\\",mountpoint!~\\\"/run/user.*\\\"}))\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"inode分区使用率最大值\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\",\"decimals\":1},\"thresholds\":{\"steps\":[{\"value\":50,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":1,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(node_filesystem_device_error{instance=\\\"$node\\\",mountpoint!~\\\"/var/lib/.*\\\",mountpoint!~\\\"/run.*\\\"})\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"写文件错误数总和\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":30}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":0,\"to\":0},\"result\":{\"color\":\"#369903\"}},{\"type\":\"range\",\"match\":{\"from\":1,\"to\":10000},\"result\":{\"color\":\"#f0310f\"}}],\"standardOptions\":{\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":18,\"y\":0,\"i\":\"9\"}}", - "weight": 0 - } - ] - }, - { - "name": "系统指标", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"node_procs_running{instance=\\\"$node\\\"}\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"进程数\",\"description\":\"进程数超过2000,可以考虑扩容了\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[{\"value\":2000,\"color\":\"#ff0000\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_timex_offset_seconds{instance=\\\"$node\\\"}\",\"legend\":\"ntp偏移\"}],\"name\":\"NTP偏移\",\"description\":\"\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_intr_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"Interrupts\"},{\"expr\":\"irate(node_context_switches_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"context switches\"}],\"name\":\"上下文切换/中断\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_entropy_available_bits{instance=\\\"$node\\\"}\",\"legend\":\"entropy\"}],\"name\":\"熵池大小\",\"description\":\"熵池太小 ,程序使用随机函数会阻塞,可以安装 rng-tools 工具增加熵池大小,可参考\\nhttps://codeantenna.com/a/Ab6aMd3NSA \",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[{\"value\":100,\"color\":\"#f70202\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "CPU详情", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\" (avg by (mode)(rate(node_cpu_seconds_total{instance=\\\"$node\\\",mode!=\\\"idle\\\"}[1m])))*100\",\"legend\":\"{{mode}}\"}],\"name\":\"CPU使用率详情\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\" (avg by (mode)(rate(node_cpu_seconds_total{instance=\\\"$node\\\",mode=\\\"idle\\\"}[1m])))*100\",\"legend\":\"cpu_idle\"}],\"name\":\"CPU空闲率\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[{\"value\":10,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_load1{instance=\\\"$node\\\"}\",\"legend\":\"load1\"},{\"expr\":\"node_load5{instance=\\\"$node\\\"}\",\"legend\":\"load5\"},{\"expr\":\"node_load15{instance=\\\"$node\\\"}\",\"legend\":\"load15\"}],\"name\":\"CPU负载\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "内存详情", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_HugePages_Total{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Total\"},{\"expr\":\"node_memory_Hugepagesize_bytes{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Size\"},{\"expr\":\"node_memory_HugePages_Surp{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Surp \"},{\"expr\":\"node_memory_HugePages_Free{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Free\"},{\"expr\":\"node_memory_HugePages_Rsvd{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Rsvd\"},{\"expr\":\"node_memory_AnonHugePages_bytes{instance=\\\"$node\\\"}\",\"legend\":\"AnonHugePages\"},{\"expr\":\"node_memory_Inactive_file_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Inactive_file\"},{\"expr\":\"node_memory_Inactive_anon_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Inactive_anon\"},{\"expr\":\"node_memory_Active_file_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Active_file\"},{\"expr\":\"node_memory_Active_anon_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Active_anon\"},{\"expr\":\"node_memory_Unevictable_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Unevictable\"},{\"expr\":\"node_memory_AnonPages_bytes{instance=\\\"$node\\\"}\",\"legend\":\"AnonPages\"},{\"expr\":\"node_memory_Shmem_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Shmem\"},{\"expr\":\"node_memory_Mapped_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Mapped\"},{\"expr\":\"node_memory_Cached_bytes{instance=\\\"$node\\\"} \",\"legend\":\"Cache\"},{\"expr\":\"node_memory_SwapCached_bytes{instance=\\\"$node\\\"}\",\"legend\":\"SwapCache\"},{\"expr\":\"node_memory_Mlocked_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Mlocked\"},{\"expr\":\"node_memory_Buffers_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Buffers\"}],\"name\":\"用户态内存使用\",\"description\":\"内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) \",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.35,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_Slab_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Slab \"},{\"expr\":\"node_memory_SReclaimable_bytes{instance=\\\"$node\\\"}\",\"legend\":\"SReclaimable \"},{\"expr\":\"node_memory_SUnreclaim_bytes{instance=\\\"$node\\\"}\",\"legend\":\"SUnreclaim \"},{\"expr\":\"node_memory_VmallocUsed_bytes{instance=\\\"$node\\\"}\",\"legend\":\"VmallocUsed\"},{\"expr\":\"node_memory_VmallocChunk_bytes{instance=\\\"$node\\\"}\",\"legend\":\"VmallocChunk\"},{\"expr\":\"node_memory_KernelStack_bytes{instance=\\\"$node\\\"}\",\"legend\":\"KernelStack\"},{\"expr\":\"node_memory_Bounce_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Bounce \"}],\"name\":\"内核态内存使用\",\"description\":\"内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) \",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_DirectMap1G_bytes{instance=\\\"$node\\\"}\",\"legend\":\"DirectMap1G\"},{\"expr\":\"node_memory_DirectMap2M_bytes{instance=\\\"$node\\\"}\",\"legend\":\"DirectMap2M\"},{\"expr\":\"node_memory_DirectMap4k_bytes{instance=\\\"$node\\\"}\",\"legend\":\"DirectMap4K\"}],\"name\":\"TLB效率\",\"description\":\"/proc/meminfo中的DirectMap所统计的不是关于内存的使用,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的, 比如2MB,4MB,甚至1GB的内存页,视不同的硬件而定。”DirectMap4k”表示映射为4kB的内存数量, “DirectMap2M”表示映射为2MB的内存数量,以此类推。所以DirectMap其实是一个反映TLB效率的指标\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_NFS_Unstable_bytes{instance=\\\"$node\\\"}\",\"legend\":\"NFS Unstable\"},{\"expr\":\"node_memory_Writeback_bytes{instance=\\\"$node\\\"}\",\"legend\":\"memory_Writeback\"},{\"expr\":\"node_memory_Dirty_bytes{instance=\\\"$node\\\"}\",\"legend\":\"memory_Dirty\"}],\"name\":\"dirty page\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "磁盘详情", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"node_filesystem_avail_bytes{instance=\\\"$node\\\",device!~'rootfs', device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Available\"},{\"expr\":\"node_filesystem_free_bytes{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Free\"},{\"expr\":\"node_filesystem_size_bytes{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Total\"}],\"name\":\"磁盘空间\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":null},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filesystem_files{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - total\"},{\"expr\":\"node_filesystem_files_free{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - free\"}],\"name\":\"inode\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filefd_maximum{instance=\\\"$node\\\"}\",\"legend\":\"Max open files\"},{\"expr\":\"node_filefd_allocated{instance=\\\"$node\\\"}\",\"legend\":\"Open files\"}],\"name\":\"fd使用\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filesystem_readonly{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - ReadOnly\"},{\"expr\":\"node_filesystem_device_error{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Device error\"}],\"name\":\"读写错误\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_reads_completed_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}}-reads\"},{\"expr\":\"rate(node_disk_writes_completed_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Writes\"},{\"expr\":\"rate(node_disk_reads_merged_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Read merged\"},{\"expr\":\"rate(node_disk_writes_merged_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Write merged\"}],\"name\":\"IO/Merged次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_read_bytes_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}}-Read bytes\"},{\"expr\":\"rate(node_disk_written_bytes_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Written bytes\"}],\"name\":\"读写数据大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_io_time_seconds_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}}\"}],\"name\":\"io util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(rate(node_disk_read_time_seconds_total{instance=\\\"$node\\\"}[1m]) / rate(node_disk_reads_completed_total{instance=\\\"$node\\\"}[1m])+rate(node_disk_write_time_seconds_total{instance=\\\"$node\\\"}[1m]) / rate(node_disk_writes_completed_total{instance=\\\"$node\\\"}[1m]))*1000\",\"legend\":\"{{device}}\"}],\"name\":\"io await\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.64,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(rate(node_disk_read_bytes_total{instance=\\\"$node\\\"}[1m]) + rate(node_disk_written_bytes_total{instance=\\\"$node\\\"}[1m]))\\n/\\n(rate(node_disk_reads_completed_total{instance=\\\"$node\\\"}[1m]) + rate(node_disk_writes_completed_total{instance=\\\"$node\\\"}[1m]))\",\"legend\":\"avgrq-sz\"}],\"name\":\"avgrq-sz\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":4,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_io_time_weighted_seconds_total{instance=\\\"$node\\\"}[1m])\\n\",\"legend\":\"{{device}}\"}],\"name\":\"avgqu-sz\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":4,\"i\":\"9\"}}", - "weight": 0 - } - ] - }, - { - "name": "网络详情", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_bytes_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])*8\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_bytes_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])*8\",\"legend\":\"{{device}} - out\"}],\"name\":\"出入流量大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_packets_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_packets_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - out\"}],\"name\":\"packets\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_errs_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_errs_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - out\"}],\"name\":\"error\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_drop_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_drop_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - out\"}],\"name\":\"drop\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_nf_conntrack_entries{instance=\\\"$node\\\"}\",\"legend\":\"NF conntrack entries\"},{\"expr\":\"node_nf_conntrack_entries_limit{instance=\\\"$node\\\"}\",\"legend\":\"NF conntrack limit\"}],\"name\":\"nf_conntrack\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_sockstat_TCP_alloc{instance=\\\"$node\\\"}\",\"legend\":\"TCP_alloc\"},{\"expr\":\"node_sockstat_TCP_inuse{instance=\\\"$node\\\"}\",\"legend\":\"TCP_inuse\"},{\"expr\":\"node_sockstat_TCP_orphan{instance=\\\"$node\\\"}\",\"legend\":\"TCP_orphan\"},{\"expr\":\"node_sockstat_TCP_tw{instance=\\\"$node\\\"}\",\"legend\":\"TCP_tw\"},{\"expr\":\"node_netstat_Tcp_CurrEstab{instance=\\\"$node\\\"}\",\"legend\":\"TCP_CurrEstab\"}],\"name\":\"tcp\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.27,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_sockstat_sockets_used{instance=\\\"$node\\\"}\",\"legend\":\"Sockets_used\"}],\"name\":\"socket\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - } - ] - } - ] - } -] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/linux_by_telegraf.json b/docker/n9eetc/dashboards/linux_by_telegraf.json deleted file mode 100644 index 990a68f8fd37d408cde3058189e7e3761bfb96b5..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/linux_by_telegraf.json +++ /dev/null @@ -1,1523 +0,0 @@ -{ - "name": "HOST - Telegraf 模板", - "tags": "", - "configs": { - "var": [ - { - "name": "ident", - "definition": "label_values(system_load1,ident)" - } - ], - "links": [ - { - "title": "n9e", - "url": "https://n9e.gitee.io/", - "targetBlank": true - }, - { - "title": "author", - "url": "http://flashcat.cloud/", - "targetBlank": true - } - ], - "version": "2.0.0", - "panels": [ - { - "id": "0f6a1394-7cf9-4958-bcfe-2fbb59e77c12", - "type": "row", - "name": "整体概况", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 0, - "i": "0f6a1394-7cf9-4958-bcfe-2fbb59e77c12" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "count(system_load1)" - } - ], - "name": "监控机器数", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 50 - } - }, - "options": { - "standardOptions": {} - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 0, - "y": 1, - "i": "877b6db5-e82c-499a-9ebc-8ad72c2891a8" - }, - "id": "877b6db5-e82c-499a-9ebc-8ad72c2891a8" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, mem_used_percent)" - } - ], - "name": "内存率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 9, - "x": 3, - "y": 1, - "i": "29a3e6ae-d278-49b3-972b-f12a6c7c091c" - }, - "id": "29a3e6ae-d278-49b3-972b-f12a6c7c091c" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (100-cpu_usage_idle{cpu=\"cpu-total\"}))" - } - ], - "name": "cpu使用率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 12, - "y": 1, - "i": "9f2a24d5-d19f-4651-b76d-add6b9011821" - }, - "id": "9f2a24d5-d19f-4651-b76d-add6b9011821" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (disk_used_percent{path!~\"/var.*\"}))", - "legend": "{{ident}}-{{path}}" - } - ], - "name": "磁盘分区使用率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 0, - "y": 2, - "i": "dcd60296-db84-4562-99f3-2829c2f064a4" - }, - "id": "dcd60296-db84-4562-99f3-2829c2f064a4" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (rate(diskio_io_time[1m])/10))", - "legend": "" - } - ], - "name": "设备io util top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 12, - "y": 2, - "i": "ef7df29d-7dce-4788-ae42-d21d842c67d6" - }, - "id": "ef7df29d-7dce-4788-ae42-d21d842c67d6" - }, - { - "id": "7b2c5cb2-fe3b-4596-95a1-37da06cd6498", - "type": "row", - "name": "单机概况", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 5, - "i": "7b2c5cb2-fe3b-4596-95a1-37da06cd6498" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "system_uptime{ident=\"$ident\"}" - } - ], - "name": "启动时长", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [], - "standardOptions": { - "util": "humantimeSeconds", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 0, - "y": 6, - "i": "50f09231-fc5e-4f6d-9367-a3158504689b" - }, - "id": "50f09231-fc5e-4f6d-9367-a3158504689b" - }, - { - "targets": [ - { - "refId": "A", - "expr": "100-cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}" - } - ], - "name": "CPU使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 6, - "y": 6, - "i": "d44e951d-c333-4ed9-9303-9c8d29da7993" - }, - "id": "d44e951d-c333-4ed9-9303-9c8d29da7993" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_used_percent{ident=\"$ident\"}" - } - ], - "name": "内存使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 12, - "y": 6, - "i": "278c2fa1-0b19-4718-8b12-fb1c2e776258" - }, - "id": "278c2fa1-0b19-4718-8b12-fb1c2e776258" - }, - { - "targets": [ - { - "refId": "A", - "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}/linux_sysctl_fs_file_max{ident=\"$ident\"}*100" - } - ], - "name": "FD使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 25 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 2 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 18, - "y": 6, - "i": "484afcd4-7b25-4af1-8e95-88cc675f7f43" - }, - "id": "484afcd4-7b25-4af1-8e95-88cc675f7f43" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_swap_total{ident=\"$ident\"}-mem_swap_free{ident=\"$ident\"}" - } - ], - "name": "SWAP使用", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 40 - } - }, - "options": { - "valueMappings": [], - "standardOptions": { - "util": "bytesIEC", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 21, - "y": 6, - "i": "142f63d7-4979-4354-81b5-a9c5ec81fae9" - }, - "id": "142f63d7-4979-4354-81b5-a9c5ec81fae9" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_used_percent{ident=\"$ident\"}", - "legend": "{{path}}" - } - ], - "name": "磁盘使用率", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 0, - "y": 7, - "i": "4d6ec15c-8fdd-47db-a9f7-a57f03009e66" - }, - "id": "4d6ec15c-8fdd-47db-a9f7-a57f03009e66" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_inodes_used{ident=\"$ident\"}/disk_inodes_total{ident=\"$ident\"}", - "legend": "{{path}}" - } - ], - "name": "inode使用率", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 8, - "y": 7, - "i": "2991ad6b-c219-4f1d-b298-e195cf35cfec" - }, - "id": "2991ad6b-c219-4f1d-b298-e195cf35cfec" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_io_time{ident=\"$ident\"}[1m])/10", - "legend": "{{name}}" - } - ], - "name": "io_util", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 16, - "y": 7, - "i": "935435db-5a1e-4330-b95f-825e91e9d99e" - }, - "id": "935435db-5a1e-4330-b95f-825e91e9d99e" - }, - { - "id": "1a19ca3f-3296-43ef-a36c-523ead023489", - "type": "row", - "name": "系统指标", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 10, - "i": "1a19ca3f-3296-43ef-a36c-523ead023489" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "processes_total{ident=\"$ident\"}" - } - ], - "name": "进程总数", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 2000, - "color": "#fa2a05" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 11, - "i": "bab39b5e-63cf-4e88-a474-4ea8f2585d8e" - }, - "id": "bab39b5e-63cf-4e88-a474-4ea8f2585d8e" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(kernel_context_switches{ident=\"$ident\"}[1m])", - "legend": "context_switches" - }, - { - "expr": "rate(kernel_interrupts{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "kernel_interrupts" - } - ], - "name": "上下文切换/中断", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 11, - "i": "0ea75485-cc11-4b44-b13f-911429d9e103" - }, - "id": "0ea75485-cc11-4b44-b13f-911429d9e103" - }, - { - "targets": [ - { - "refId": "A", - "expr": "kernel_entropy_avail{ident=\"$ident\"}", - "legend": "entropy_avail" - } - ], - "name": "熵池大小", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 100, - "color": "#f50505" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 11, - "i": "32d764fa-ed86-4099-b0f8-1cb8c7f67315" - }, - "id": "32d764fa-ed86-4099-b0f8-1cb8c7f67315" - }, - { - "id": "fe779989-795e-4ef6-9280-fdea929bb397", - "type": "row", - "name": "CPU", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 18, - "i": "fe779989-795e-4ef6-9280-fdea929bb397" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}", - "legend": "cpu_usage_idle" - } - ], - "name": "CPU空闲率", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 10, - "color": "#f20202" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 19, - "i": "f5b86c4f-2104-41a4-9d01-a62ee64c04ff" - }, - "id": "f5b86c4f-2104-41a4-9d01-a62ee64c04ff" - }, - { - "targets": [ - { - "refId": "A", - "expr": "cpu_usage_guest{ident=\"$ident\",cpu=\"cpu-total\"}", - "legend": "" - }, - { - "expr": "cpu_usage_iowait{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "B", - "legend": "" - }, - { - "expr": "cpu_usage_user{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "C" - }, - { - "expr": "cpu_usage_system{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "D" - }, - { - "expr": "cpu_usage_irq{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "E" - }, - { - "expr": "cpu_usage_softirq{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "F" - }, - { - "expr": "cpu_usage_nice{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "G" - }, - { - "expr": "cpu_usage_steal{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "H" - } - ], - "name": "CPU使用率详情", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 19, - "i": "e833715f-065c-4b1b-9f0d-e1223b6992b8" - }, - "id": "e833715f-065c-4b1b-9f0d-e1223b6992b8" - }, - { - "targets": [ - { - "refId": "A", - "expr": "system_load15{ident=\"$ident\"}" - }, - { - "expr": "system_load1{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "system_load5{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "CPU负载", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 19, - "i": "b19f9420-9ce2-4d3c-86bf-fc247c8b760e" - }, - "id": "b19f9420-9ce2-4d3c-86bf-fc247c8b760e" - }, - { - "id": "e9ebdac6-4a87-4a79-b125-e5a258a968d0", - "type": "row", - "name": "内存详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 26, - "i": "e9ebdac6-4a87-4a79-b125-e5a258a968d0" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_active{ident=\"$ident\"}" - }, - { - "expr": "mem_cached{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "mem_buffered{ident=\"$ident\"}", - "refId": "C" - }, - { - "expr": "mem_inactive{ident=\"$ident\"}", - "refId": "D" - }, - { - "expr": "mem_mapped{ident=\"$ident\"}", - "refId": "E" - }, - { - "expr": "mem_shared{ident=\"$ident\"}", - "refId": "F" - }, - { - "expr": "mem_swap_cached{ident=\"$ident\"}", - "refId": "G" - } - ], - "name": "用户态内存使用", - "description": "内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) ", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 12, - "x": 0, - "y": 27, - "i": "655608d2-0d6d-46ed-9e6a-c482edaeacec" - }, - "id": "655608d2-0d6d-46ed-9e6a-c482edaeacec" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_slab{ident=\"$ident\"}" - }, - { - "expr": "mem_sreclaimable{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "mem_sunreclaim{ident=\"$ident\"}", - "refId": "C" - }, - { - "expr": "mem_vmalloc_used{ident=\"$ident\"}", - "refId": "D" - }, - { - "expr": "mem_vmalloc_chunk{ident=\"$ident\"}", - "refId": "E" - } - ], - "name": "内核态内存使用", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 12, - "x": 12, - "y": 27, - "i": "d57fe702-e21f-45ee-9b36-31695e698059" - }, - "id": "d57fe702-e21f-45ee-9b36-31695e698059" - }, - { - "id": "893315c8-54ac-4eaf-9072-d0a2debd3404", - "type": "row", - "name": "磁盘详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 34, - "i": "893315c8-54ac-4eaf-9072-d0a2debd3404" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_free{ident=\"$ident\"}" - }, - { - "expr": "disk_total{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "disk_used{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "磁盘空间", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": null - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 35, - "i": "b39143a6-9ca8-4732-9100-0a8c029440b5" - }, - "id": "b39143a6-9ca8-4732-9100-0a8c029440b5" - }, - { - "targets": [ - { - "refId": "A", - "expr": "linux_sysctl_fs_file_max{ident=\"$ident\"}" - }, - { - "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}", - "refId": "B" - } - ], - "name": "fd使用", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 35, - "i": "3a13d95d-a311-4b0b-87f2-4216cac9a533" - }, - "id": "3a13d95d-a311-4b0b-87f2-4216cac9a533" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_inodes_total{ident=\"$ident\",path!~\"/var.*\"}", - "legend": "{{path}}-total" - }, - { - "expr": "disk_inodes_used{ident=\"$ident\",path!~\"/var.*\"}", - "refId": "B", - "legend": "{{path}}-used" - } - ], - "name": "inode", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 35, - "i": "6a1fa455-a385-456a-a32b-30119082f453" - }, - "id": "6a1fa455-a385-456a-a32b-30119082f453" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_reads{ident=\"$ident\"}[1m])", - "legend": "{{name}}-read" - }, - { - "expr": "rate(diskio_writes{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{name}}-writes" - } - ], - "name": "IOPS", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 37, - "i": "c74e5155-0e3c-4cb4-8e57-ce8af46ddf90" - }, - "id": "c74e5155-0e3c-4cb4-8e57-ce8af46ddf90" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_read_bytes{ident=\"$ident\"}[1m])", - "legend": "{{name}}-read" - }, - { - "expr": "rate(diskio_write_bytes{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{name}}-writes" - } - ], - "name": "IO吞吐量", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 37, - "i": "c522e6f5-fb3d-4fc0-9ae6-7ec002e55e95" - }, - "id": "c522e6f5-fb3d-4fc0-9ae6-7ec002e55e95" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_write_time{ident=\"$ident\"}[1m])/rate(diskio_writes{ident=\"$ident\"}[1m])+rate(diskio_read_time{ident=\"$ident\"}[1m])/rate(diskio_reads{ident=\"$ident\"}[1m])", - "legend": "{{name}}" - } - ], - "name": "iowait", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 37, - "i": "47fd7f57-ed4a-43cf-86e3-628c2f697769" - }, - "id": "47fd7f57-ed4a-43cf-86e3-628c2f697769" - }, - { - "id": "f8c5e284-5e23-4646-976c-23511f4f908d", - "type": "row", - "name": "网络详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 44, - "i": "f8c5e284-5e23-4646-976c-23511f4f908d" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_bytes_recv{ident=\"$ident\",interface=~\"eth.*\"}[1m])*8", - "legend": "{{interface}}-recv" - }, - { - "expr": "rate(net_bytes_sent{ident=\"$ident\",interface=~\"eth.*\"}[1m])*8", - "refId": "B", - "legend": "{{interface}}-sent" - } - ], - "name": "网络流量", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 0, - "y": 45, - "i": "7fe8774f-7d03-4514-a6b6-b626d2a95265" - }, - "id": "7fe8774f-7d03-4514-a6b6-b626d2a95265" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_packets_recv{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "legend": "{{interface}}-recv" - }, - { - "expr": "rate(net_packets_sent{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "refId": "B", - "legend": "{{interface}}-sent" - } - ], - "name": "packets", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 6, - "y": 45, - "i": "d030e5e7-06cd-42e9-b1e5-0c32b51f853e" - }, - "id": "d030e5e7-06cd-42e9-b1e5-0c32b51f853e" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_err_in{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "legend": "{{interface}}-in" - }, - { - "expr": "rate(net_err_out{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "refId": "B", - "legend": "{{interface}}-out" - } - ], - "name": "error", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 12, - "y": 45, - "i": "330f5fd9-aca5-4619-b81b-33203256d560" - }, - "id": "330f5fd9-aca5-4619-b81b-33203256d560" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_drop_in{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "legend": "{{interface}}-in" - }, - { - "expr": "rate(net_drop_out{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "refId": "B", - "legend": "{{interface}}-out" - } - ], - "name": "drop", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 18, - "y": 45, - "i": "34a73b20-56d7-4edb-b6f7-acc93d00a026" - }, - "id": "34a73b20-56d7-4edb-b6f7-acc93d00a026" - }, - { - "targets": [ - { - "refId": "A", - "expr": "netstat_tcp_established{ident=\"$ident\"}" - }, - { - "expr": "netstat_tcp_listen{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "netstat_tcp_time_wait{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "tcp", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 24, - "x": 0, - "y": 47, - "i": "7664d34f-7bcf-4431-a6a7-4d924d2e176d" - }, - "id": "7664d34f-7bcf-4431-a6a7-4d924d2e176d" - } - ] - } -} \ No newline at end of file diff --git a/docker/n9eetc/dashboards/mongo_by_exporter.json b/docker/n9eetc/dashboards/mongo_by_exporter.json deleted file mode 100644 index 2200145dc5631e695b9955b799a2f296ee60c965..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/mongo_by_exporter.json +++ /dev/null @@ -1,109 +0,0 @@ -[ - { - "name": "MongoDB Overview - 模板", - "tags": "Prometheus MongoDB", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(mongodb_up,instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_up{instance=\\\"$instance\\\"}\",\"time\":{\"num\":1,\"unit\":\"hour\",\"description\":\"小时\"}}],\"name\":\"Up\",\"description\":\"实例数\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":1},\"result\":{\"color\":\"#53b503\"}},{\"type\":\"range\",\"match\":{\"special\":null,\"from\":0,\"to\":1},\"result\":{\"color\":\"#e70d0d\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_instance_uptime_seconds{instance='$instance'}\",\"time\":{\"num\":1,\"unit\":\"hour\",\"description\":\"小时\"}}],\"name\":\"Uptime\",\"description\":\"启用时长\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"title\":null}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#ec7718\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#53b503\"}}],\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_memory{instance='$instance'} * 1024 * 1024\",\"legend\":\"{{type}}\"}],\"name\":\"Memory\",\"description\":\"内存占用(MiB)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_extra_info_page_faults_total{instance=\\\"$instance\\\"}[5m])\",\"legend\":\"{{type}}\"}],\"name\":\"Page Faults\",\"description\":\"页缺失中断次数 Page faults indicate that requests are processed from disk either because an index is missing or there is not enough memory for the data set. Consider increasing memory or sharding out.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\",\"decimals\":null},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_ss_network_bytesOut{instance='$instance'}[5m])\",\"legend\":\"bytesOut\"},{\"expr\":\"rate(mongodb_ss_network_bytesIn{instance='$instance'}[5m])\",\"refId\":\"B\",\"legend\":\"bytesIn\"}],\"name\":\"Network I/O\",\"description\":\"网络流量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_connections{instance=\\\"$instance\\\", state=\\\"current\\\"}\",\"legend\":\"Connections\"}],\"name\":\"Connections\",\"description\":\"连接数 Keep in mind the hard limit on the maximum number of connections set by your distribution.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_asserts_total{instance=\\\"$instance\\\"}[5m])\",\"legend\":\"{{type}}\"}],\"name\":\"Assert Events\",\"description\":\"断言错误次数 Asserts are not important by themselves, but you can correlate spikes with other graphs.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_mongod_global_lock_current_queue{instance=\\\"$instance\\\"}\",\"legend\":\"{{type}}\"}],\"name\":\"Lock Queue\",\"description\":\"等待获取锁操作数量 Any number of queued operations for long periods of time is an indication of possible issues. Find the cause and fix it before requests get stuck in the queue.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":2,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "Operation Info", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_op_counters_total{instance=\\\"$instance\\\", type!=\\\"command\\\"}[5m])\",\"legend\":\"{{type}}\"},{\"expr\":\"rate(mongodb_mongod_op_counters_repl_total{instance=\\\"$instance\\\", type!~\\\"(command|query|getmore)\\\"}[5m]) or \\nrate(mongodb_mongos_op_counters_repl_total{instance=\\\"$instance\\\", type!~\\\"(command|query|getmore)\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"repl_{{type}}\"},{\"expr\":\"rate(mongodb_mongod_metrics_ttl_deleted_documents_total{instance=\\\"$instance\\\"}[5m]) or \\nrate(mongodb_mongos_metrics_ttl_deleted_documents_total{instance=\\\"$instance\\\"}[5m])\",\"refId\":\"C\",\"legend\":\"ttl_delete\"}],\"name\":\"Command Operations\",\"description\":\"接收请求数 Shows how many times a command is executed per second on average during the selected interval.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_mongod_metrics_document_total{instance=\\\"$instance\\\"}[5m])\",\"legend\":\"{{state}}\"}],\"name\":\"Document Operations\",\"description\":\"文档操作数 When used in combination with 'Command Operations', this graph can help identify write amplification. For example, when one insert or update command actually inserts or updates hundreds, thousands, or even millions of documents.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_mongod_op_latencies_latency_total{instance='$instance'}[5m]) / rate(mongodb_mongod_op_latencies_ops_total{instance='$instance'}[5m]) / 1000\",\"legend\":\"{{type}}\"}],\"name\":\"Response Time\",\"description\":\"操作详情耗时(毫秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"milliseconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(increase(mongodb_mongod_metrics_query_executor_total{instance=\\\"$instance\\\", state=\\\"scanned_objects\\\"}[5m])) / sum(increase(mongodb_mongod_metrics_document_total{instance=\\\"$instance\\\", state=\\\"returned\\\"}[5m]))\",\"legend\":\"Document\"},{\"expr\":\"sum(increase(mongodb_mongod_metrics_query_executor_total{instance=\\\"$instance\\\", state=\\\"scanned\\\"}[5m])) / sum(increase(mongodb_mongod_metrics_document_total{instance=\\\"$instance\\\", state=\\\"returned\\\"}[5m]))\",\"refId\":\"B\",\"legend\":\"Index\"}],\"name\":\"Query Efficiency\",\"description\":\"查询效率\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_mongod_metrics_cursor_open{instance=\\\"$instance\\\"}\",\"legend\":\"{{state}}\"}],\"name\":\"Cursors\",\"description\":\"游标数量 Helps identify why connections are increasing. Shows active cursors compared to cursors being automatically killed after 10 minutes due to an application not closing the connection.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "Cache Info", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_mongod_wiredtiger_cache_bytes{instance='$instance'}\",\"legend\":\"{{type}}\"}],\"name\":\"Cache Size\",\"description\":\"缓存大小(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_mongod_wiredtiger_cache_bytes_total{instance='$instance'}[5m])\",\"legend\":\"{{type}}\"}],\"name\":\"Cache I/O\",\"description\":\"写入或读取的缓存数据大小(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"100 * sum(mongodb_mongod_wiredtiger_cache_pages{instance='$instance',type=\\\"dirty\\\"}) / sum(mongodb_mongod_wiredtiger_cache_pages{instance='$instance',type=\\\"total\\\"})\",\"legend\":\"dirty rate\"}],\"name\":\"Cache Dirty Pages Rate\",\"description\":\"缓存脏页占比\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(mongodb_mongod_wiredtiger_cache_evicted_total{instance='$instance'}[5m])\",\"legend\":\"evicted pages\"}],\"name\":\"Cache Evicted Pages\",\"description\":\"缓存剔除页数量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "ReplSet Info", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"time() - mongodb_mongod_replset_member_election_date\"}],\"name\":\"Replset Election\",\"description\":\"副本集选主时间\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#f24526\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#53b503\"}}],\"standardOptions\":{\"util\":\"seconds\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"mongodb_mongod_replset_member_replication_lag{instance=\\\"$instance\\\"}\",\"legend\":\"lag\"}],\"name\":\"Replset Lag Seconds\",\"description\":\"副本集成员主从同步延迟\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/mysql_by_categraf.json b/docker/n9eetc/dashboards/mysql_by_categraf.json deleted file mode 100644 index 0c31c14782b34a78d0c3b3f85ef780b5a06b5bc0..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/mysql_by_categraf.json +++ /dev/null @@ -1,119 +0,0 @@ -[ - { - "name": "MySQL Overview - 模板", - "tags": "Prometheus MySQL", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(mysql_global_status_uptime, instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(mysql_global_status_uptime{instance=~\\\"$instance\\\"})\"}],\"name\":\"MySQL Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#ec7718\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#369603\"}}],\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_queries{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Current QPS\",\"description\":\"mysql_global_status_queries\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":100},\"result\":{\"color\":\"#05a31f\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#ea3939\"}}],\"standardOptions\":{\"decimals\":2}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"avg(mysql_global_variables_innodb_buffer_pool_size{instance=~\\\"$instance\\\"})\"}],\"name\":\"InnoDB Buffer Pool\",\"description\":\"**InnoDB Buffer Pool Size**\\n\\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(increase(mysql_global_status_table_locks_waited{instance=~\\\"$instance\\\"}[5m]))\"}],\"name\":\"Table Locks Waited(5min)\",\"description\":\"**Table Locks**\\n\\nMySQL takes a number of different locks for varying reasons. In this graph we see how many Table level locks MySQL has requested from the storage engine. In the case of InnoDB, many times the locks could actually be row locks as it only takes table level locks in a few specific cases.\\n\\nIt is most useful to compare Locks Immediate and Locks Waited. If Locks waited is rising, it means you have lock contention. Otherwise, Locks Immediate rising and falling is normal activity.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":1},\"result\":{\"color\":\"#e70d0d\"}},{\"type\":\"range\",\"match\":{\"to\":1},\"result\":{\"color\":\"#53b503\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Connections", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Connections\"},{\"expr\":\"sum(mysql_global_status_max_used_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Used Connections\"},{\"expr\":\"sum(mysql_global_variables_max_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Connections\"},{\"expr\":\"sum(rate(mysql_global_status_aborted_connects{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Aborted Connections\"}],\"name\":\"MySQL Connections\",\"description\":\"**Max Connections** \\n\\nMax Connections is the maximum permitted number of simultaneous client connections. By default, this is 151. Increasing this value increases the number of file descriptors that mysqld requires. If the required number of descriptors are not available, the server reduces the value of Max Connections.\\n\\nmysqld actually permits Max Connections + 1 clients to connect. The extra connection is reserved for use by accounts that have the SUPER privilege, such as root.\\n\\nMax Used Connections is the maximum number of connections that have been in use simultaneously since the server started.\\n\\nConnections is the number of connection attempts (successful or not) to the MySQL server.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Connected\"},{\"expr\":\"sum(mysql_global_status_threads_running{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Running\"}],\"name\":\"MySQL Client Thread Activity\",\"description\":\"Threads Connected is the number of open connections, while Threads Running is the number of threads not sleeping.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Query Performance", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_created_tmp_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_disk_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Disk Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_files{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Files\"}],\"name\":\"MySQL Temporary Objects\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.64,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_select_full_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_full_range_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Range Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_range{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range\"},{\"expr\":\"sum(rate(mysql_global_status_select_range_check{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range Check\"},{\"expr\":\"sum(rate(mysql_global_status_select_scan{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Scan\"}],\"name\":\"MySQL Select Types\",\"description\":\"**MySQL Select Types**\\n\\nAs with most relational databases, selecting based on indexes is more efficient than scanning an entire table's data. Here we see the counters for selects not done with indexes.\\n\\n* ***Select Scan*** is how many queries caused full table scans, in which all the data in the table had to be read and either discarded or returned.\\n* ***Select Range*** is how many queries used a range scan, which means MySQL scanned all rows in a given range.\\n* ***Select Full Join*** is the number of joins that are not joined on an index, this is usually a huge performance hit.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"stack\":\"off\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.41},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_sort_rows{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Rows\"},{\"expr\":\"sum(rate(mysql_global_status_sort_range{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Range\"},{\"expr\":\"sum(rate(mysql_global_status_sort_merge_passes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Merge Passes\"},{\"expr\":\"sum(rate(mysql_global_status_sort_scan{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Scan\"}],\"name\":\"MySQL Sorts\",\"description\":\"**MySQL Sorts**\\n\\nDue to a query's structure, order, or other requirements, MySQL sorts the rows before returning them. For example, if a table is ordered 1 to 10 but you want the results reversed, MySQL then has to sort the rows to return 10 to 1.\\n\\nThis graph also shows when sorts had to scan a whole table or a given range of a table in order to return the results and which could not have been sorted via an index.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_slow_queries{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Slow Queries\"}],\"name\":\"MySQL Slow Queries\",\"description\":\"**MySQL Slow Queries**\\n\\nSlow queries are defined as queries being slower than the long_query_time setting. For example, if you have long_query_time set to 3, all queries that take longer than 3 seconds to complete will show on this graph.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"bars\",\"stack\":\"off\",\"fillOpacity\":0.81},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_bytes_received{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Inbound\"},{\"expr\":\"sum(rate(mysql_global_status_bytes_sent{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Outbound\"}],\"name\":\"MySQL Network Traffic\",\"description\":\"**MySQL Network Traffic**\\n\\nHere we can see how much network traffic is generated by MySQL. Outbound is network traffic sent from MySQL and Inbound is network traffic MySQL has received.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands, Handlers", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"topk(10, rate(mysql_global_status_commands_total{instance=~\\\"$instance\\\"}[5m])>0)\",\"legend\":\"Com_{{command}}\"}],\"name\":\"Top Command Counters\",\"description\":\"**Top Command Counters**\\n\\nThe Com_{{xxx}} statement counter variables indicate the number of times each xxx statement has been executed. There is one status variable for each type of statement. For example, Com_delete and Com_update count [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements, respectively. Com_delete_multi and Com_update_multi are similar but apply to [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements that use multiple-table syntax.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.2,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler!~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Handlers\",\"description\":\"**MySQL Handlers**\\n\\nHandler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes.\\n\\nThis is in fact the layer between the Storage Engine and MySQL.\\n\\n* `read_rnd_next` is incremented when the server performs a full table scan and this is a counter you don't really want to see with a high value.\\n* `read_key` is incremented when a read is done with an index.\\n* `read_next` is incremented when the storage engine is asked to 'read the next index entry'. A high value means a lot of index scans are being done.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":3},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler=~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Transaction Handlers\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Open Files", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_variables_open_files_limit{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Files Limit\"},{\"expr\":\"mysql_global_status_open_files{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Files\"}],\"name\":\"MySQL Open Files\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Table Openings", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n/\\n(\\nrate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n+\\nrate(mysql_global_status_table_open_cache_misses{instance=~\\\"$instance\\\"}[5m])\\n)\",\"legend\":\"Table Open Cache Hit Ratio\"}],\"name\":\"Table Open Cache Hit Ratio Mysql 5.6.6+\",\"description\":\"**MySQL Table Open Cache Status**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_status_open_tables{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Tables\"},{\"expr\":\"mysql_global_variables_table_open_cache{instance=~\\\"$instance\\\"}\",\"legend\":\"Table Open Cache\"}],\"name\":\"MySQL Open Tables\",\"description\":\"**MySQL Open Tables**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/mysql_by_exporter.json b/docker/n9eetc/dashboards/mysql_by_exporter.json deleted file mode 100644 index b76ef591b2c5275203e5afdd12f6316fc795cf4c..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/mysql_by_exporter.json +++ /dev/null @@ -1,119 +0,0 @@ -[ - { - "name": "MySQL Overview - 模板", - "tags": "Prometheus MySQL", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(mysql_global_status_uptime, instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(mysql_global_status_uptime{instance=~\\\"$instance\\\"})\"}],\"name\":\"MySQL Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#ec7718\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#369603\"}}],\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_queries{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Current QPS\",\"description\":\"mysql_global_status_queries\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":100},\"result\":{\"color\":\"#05a31f\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#ea3939\"}}],\"standardOptions\":{\"decimals\":2}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"avg(mysql_global_variables_innodb_buffer_pool_size{instance=~\\\"$instance\\\"})\"}],\"name\":\"InnoDB Buffer Pool\",\"description\":\"**InnoDB Buffer Pool Size**\\n\\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(increase(mysql_global_status_table_locks_waited{instance=~\\\"$instance\\\"}[5m]))\"}],\"name\":\"Table Locks Waited(5min)\",\"description\":\"**Table Locks**\\n\\nMySQL takes a number of different locks for varying reasons. In this graph we see how many Table level locks MySQL has requested from the storage engine. In the case of InnoDB, many times the locks could actually be row locks as it only takes table level locks in a few specific cases.\\n\\nIt is most useful to compare Locks Immediate and Locks Waited. If Locks waited is rising, it means you have lock contention. Otherwise, Locks Immediate rising and falling is normal activity.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":1},\"result\":{\"color\":\"#e70d0d\"}},{\"type\":\"range\",\"match\":{\"to\":1},\"result\":{\"color\":\"#53b503\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Connections", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Connections\"},{\"expr\":\"sum(mysql_global_status_max_used_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Used Connections\"},{\"expr\":\"sum(mysql_global_variables_max_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Connections\"},{\"expr\":\"sum(rate(mysql_global_status_aborted_connects{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Aborted Connections\"}],\"name\":\"MySQL Connections\",\"description\":\"**Max Connections** \\n\\nMax Connections is the maximum permitted number of simultaneous client connections. By default, this is 151. Increasing this value increases the number of file descriptors that mysqld requires. If the required number of descriptors are not available, the server reduces the value of Max Connections.\\n\\nmysqld actually permits Max Connections + 1 clients to connect. The extra connection is reserved for use by accounts that have the SUPER privilege, such as root.\\n\\nMax Used Connections is the maximum number of connections that have been in use simultaneously since the server started.\\n\\nConnections is the number of connection attempts (successful or not) to the MySQL server.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Connected\"},{\"expr\":\"sum(mysql_global_status_threads_running{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Running\"}],\"name\":\"MySQL Client Thread Activity\",\"description\":\"Threads Connected is the number of open connections, while Threads Running is the number of threads not sleeping.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Query Performance", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_created_tmp_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_disk_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Disk Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_files{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Files\"}],\"name\":\"MySQL Temporary Objects\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.64,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_select_full_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_full_range_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Range Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_range{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range\"},{\"expr\":\"sum(rate(mysql_global_status_select_range_check{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range Check\"},{\"expr\":\"sum(rate(mysql_global_status_select_scan{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Scan\"}],\"name\":\"MySQL Select Types\",\"description\":\"**MySQL Select Types**\\n\\nAs with most relational databases, selecting based on indexes is more efficient than scanning an entire table's data. Here we see the counters for selects not done with indexes.\\n\\n* ***Select Scan*** is how many queries caused full table scans, in which all the data in the table had to be read and either discarded or returned.\\n* ***Select Range*** is how many queries used a range scan, which means MySQL scanned all rows in a given range.\\n* ***Select Full Join*** is the number of joins that are not joined on an index, this is usually a huge performance hit.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"stack\":\"off\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.41},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_sort_rows{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Rows\"},{\"expr\":\"sum(rate(mysql_global_status_sort_range{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Range\"},{\"expr\":\"sum(rate(mysql_global_status_sort_merge_passes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Merge Passes\"},{\"expr\":\"sum(rate(mysql_global_status_sort_scan{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Scan\"}],\"name\":\"MySQL Sorts\",\"description\":\"**MySQL Sorts**\\n\\nDue to a query's structure, order, or other requirements, MySQL sorts the rows before returning them. For example, if a table is ordered 1 to 10 but you want the results reversed, MySQL then has to sort the rows to return 10 to 1.\\n\\nThis graph also shows when sorts had to scan a whole table or a given range of a table in order to return the results and which could not have been sorted via an index.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_slow_queries{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Slow Queries\"}],\"name\":\"MySQL Slow Queries\",\"description\":\"**MySQL Slow Queries**\\n\\nSlow queries are defined as queries being slower than the long_query_time setting. For example, if you have long_query_time set to 3, all queries that take longer than 3 seconds to complete will show on this graph.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"bars\",\"stack\":\"off\",\"fillOpacity\":0.81},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_bytes_received{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Inbound\"},{\"expr\":\"sum(rate(mysql_global_status_bytes_sent{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Outbound\"}],\"name\":\"MySQL Network Traffic\",\"description\":\"**MySQL Network Traffic**\\n\\nHere we can see how much network traffic is generated by MySQL. Outbound is network traffic sent from MySQL and Inbound is network traffic MySQL has received.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands, Handlers", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"topk(10, rate(mysql_global_status_commands_total{instance=~\\\"$instance\\\"}[5m])>0)\",\"legend\":\"Com_{{command}}\"}],\"name\":\"Top Command Counters\",\"description\":\"**Top Command Counters**\\n\\nThe Com_{{xxx}} statement counter variables indicate the number of times each xxx statement has been executed. There is one status variable for each type of statement. For example, Com_delete and Com_update count [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements, respectively. Com_delete_multi and Com_update_multi are similar but apply to [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements that use multiple-table syntax.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.2,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler!~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Handlers\",\"description\":\"**MySQL Handlers**\\n\\nHandler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes.\\n\\nThis is in fact the layer between the Storage Engine and MySQL.\\n\\n* `read_rnd_next` is incremented when the server performs a full table scan and this is a counter you don't really want to see with a high value.\\n* `read_key` is incremented when a read is done with an index.\\n* `read_next` is incremented when the storage engine is asked to 'read the next index entry'. A high value means a lot of index scans are being done.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":3},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler=~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Transaction Handlers\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Open Files", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_variables_open_files_limit{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Files Limit\"},{\"expr\":\"mysql_global_status_innodb_num_open_files{instance=~\\\"$instance\\\"}\",\"legend\":\"InnoDB Open Files\"}],\"name\":\"MySQL Open Files\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Table Openings", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n/\\n(\\nrate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n+\\nrate(mysql_global_status_table_open_cache_misses{instance=~\\\"$instance\\\"}[5m])\\n)\",\"legend\":\"Table Open Cache Hit Ratio\"}],\"name\":\"Table Open Cache Hit Ratio\",\"description\":\"**MySQL Table Open Cache Status**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_status_open_tables{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Tables\"},{\"expr\":\"mysql_global_variables_table_open_cache{instance=~\\\"$instance\\\"}\",\"legend\":\"Table Open Cache\"}],\"name\":\"MySQL Open Tables\",\"description\":\"**MySQL Open Tables**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/net_response_by_categraf.json b/docker/n9eetc/dashboards/net_response_by_categraf.json deleted file mode 100644 index 5f96e0705a0824976815da527762d701d345d662..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/net_response_by_categraf.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "TCP探测", - "tags": "", - "configs": "", - "chart_groups": [ - { - "name": "Default chart group", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"max(net_response_result_code) by (target)\",\"legend\":\"UP?\"},{\"expr\":\"max(net_response_response_time) by (target)\",\"refId\":\"C\",\"legend\":\"latency(s)\"}],\"name\":\"Targets\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelValuesToRows\",\"aggrDimension\":\"target\"},\"options\":{\"valueMappings\":[],\"standardOptions\":{}},\"overrides\":[{\"properties\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"UP\",\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"special\":1,\"from\":1},\"result\":{\"text\":\"DOWN\",\"color\":\"#e90f0f\"}}],\"standardOptions\":{}},\"matcher\":{\"value\":\"A\"}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":4,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/oracle_by_categraf.json b/docker/n9eetc/dashboards/oracle_by_categraf.json deleted file mode 100644 index 3c6635baac7d96ed023c414c23d54414e69f4518..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/oracle_by_categraf.json +++ /dev/null @@ -1,127 +0,0 @@ -[ - { - "name": "Oracle - 模板", - "tags": "Telegraf", - "configs": "{\"var\":[{\"name\":\"ident\",\"definition\":\"label_values(oracle_up,ident)\",\"options\":[\"tt-fc-log00.nj\"]},{\"name\":\"instance\",\"definition\":\"label_values(oracle_up{ident=\\\"$ident\\\"},instance)\"}]}", - "chart_groups": [ - { - "name": "Activities", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(oracle_activity_execute_count_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}[2m])\"}],\"name\":\"execute count / second\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(oracle_activity_user_commits_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}[2m])\"}],\"name\":\"user commits / second\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(oracle_activity_user_rollbacks_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}[2m])\"}],\"name\":\"user rollbacks / second\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_up{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"status\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"text\":\"UP\",\"color\":\"#5ea70f\"}},{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"DOWN\",\"color\":\"#f60f0f\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Waits", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_wait_time_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"legend\":\"{{wait_class}}\"}],\"name\":\"Time waited\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Tablespace", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_tablespace_bytes{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}/oracle_tablespace_max_bytes{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"legend\":\"{{tablespace}}\"}],\"name\":\"Used Percent\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_tablespace_free{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"legend\":\"{{tablespace}}\"}],\"name\":\"Free space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "IO and TPS", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_io_requests_per_second_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"IO Requests / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_user_transaction_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"TPS\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_io_megabytes_per_second_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}*1024*1024\"}],\"name\":\"IO Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Connections", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sessions_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\",status=\\\"ACTIVE\\\"}\",\"legend\":\"\"}],\"name\":\"Sessions\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Hit Ratio", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_buffer_cache_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Buffer Cache Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_redo_allocation_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Redo Allocation Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_row_cache_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Row Cache Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_library_cache_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Library Cache Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Physical Read Write", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_bytes_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_Bytes_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical Read Write Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_total_bytes_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_Total_Bytes_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical Read Write Total Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_io_requests_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_IO_Requests_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical RW IO Requests / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_total_io_requests_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_Total_IO_Requests_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical RW Total IO Requests / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/ping_by_categraf.json b/docker/n9eetc/dashboards/ping_by_categraf.json deleted file mode 100644 index 4c09be0c234d46462b274561f5fa96cf55b1e0a5..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/ping_by_categraf.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "PING探测", - "tags": "", - "configs": "", - "chart_groups": [ - { - "name": "Default chart group", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"max(ping_result_code) by (target)\",\"legend\":\"UP?\"},{\"expr\":\"max(ping_percent_packet_loss) by (target)\",\"refId\":\"B\",\"legend\":\"Packet Loss %\"},{\"expr\":\"max(httpresponse_response_time) by (target)\",\"refId\":\"C\",\"legend\":\"latency(s)\"}],\"name\":\"Ping\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelValuesToRows\",\"aggrDimension\":\"target\"},\"options\":{\"valueMappings\":[],\"standardOptions\":{}},\"overrides\":[{\"properties\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"UP\",\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"special\":1,\"from\":1},\"result\":{\"text\":\"DOWN\",\"color\":\"#e90f0f\"}}],\"standardOptions\":{}},\"matcher\":{\"value\":\"A\"}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":4,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/process_by_exporter.json b/docker/n9eetc/dashboards/process_by_exporter.json deleted file mode 100644 index 4a2d6c07ac5b912e8cae37ded22b7f8b1b1bef13..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/process_by_exporter.json +++ /dev/null @@ -1,153 +0,0 @@ -[ - { - "name": "Linux Process - 模板", - "tags": "Prometheus Process", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(namedprocess_namegroup_num_procs, instance)\",\"multi\":false,\"options\":[\"tt-fc-es02.nj:12346\"]},{\"definition\":\"label_values(namedprocess_namegroup_cpu_seconds_total{instance=~\\\"$instance\\\"},groupname)\",\"name\":\"processes\",\"multi\":true,\"options\":[\"(sd-pam)\",\"NetworkManager\",\"YDLive\",\"YDPython\",\"YDService\",\"agent\",\"agetty\",\"atd\",\"auditd\",\"barad_agent\",\"bash\",\"chronyd\",\"crond\",\"dbus-daemon\",\"fc-agent\",\"fc-alert\",\"fc-checker\",\"gpg-agent\",\"less\",\"lsmd\",\"mongod\",\"mysql\",\"mysqld\",\"nginx\",\"ngo\",\"node\",\"openvpn\",\"podman pause\",\"polkitd\",\"process-agent\",\"redis-server\",\"rsyslogd\",\"sedispatch\",\"sgagent\",\"sh\",\"ssh-agent\",\"sshd\",\"sssd\",\"sssd_be\",\"sssd_nss\",\"su\",\"sudo\",\"systemd\",\"systemd-journal\",\"systemd-logind\",\"systemd-udevd\",\"tat_agent\",\"trace-agent\",\"tuned\",\"unbound-anchor\",\"vminsert-prod\",\"vmselect-prod\",\"vmstorage-prod\"],\"allOption\":true}]}", - "chart_groups": [ - { - "name": "Cpu Usage", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(rate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"user\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) + ignoring(mode) rate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])) or (irate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"user\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) + ignoring(mode) irate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Total CPU cores used\",\"description\":\"进程占用CPU时间(用户态+内核态),倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5, rate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or ( irate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by System CPU cores used\",\"description\":\"进程占用CPU时间(内核态),倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Memory Usage", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m])+ ignoring (memtype) avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m])) or (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m])+ ignoring (memtype) avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m])) ))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Used memory\",\"description\":\"进程常驻内存与交换空间平均占用容量之和,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5, (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m]) ))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Resident Memory\",\"description\":\"进程常驻内存平均占用容量之和,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m]))) \",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Swapped Memory\",\"description\":\"进程交换内存平均占用容量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"virtual\\\",instance=~\\\"$instance\\\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"virtual\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Virtual Memory\",\"description\":\"进程虚拟内存平均占用容量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Disk IO Usage", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(rate(namedprocess_namegroup_write_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_write_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Bytes Written\",\"description\":\"进程写数据量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(rate(namedprocess_namegroup_read_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_read_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Bytes Read\",\"description\":\"进程读数据量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Process and Thread Counts", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(max_over_time(namedprocess_namegroup_num_procs{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_num_procs{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by number of processes instances\",\"description\":\"同名进程数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(max_over_time(namedprocess_namegroup_num_threads{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_num_threads{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by number of threads\",\"description\":\"进程内线程数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Context Switches", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"voluntary\\\"}[5m]) or irate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"voluntary\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top Processes by Voluntary Context Switches\",\"description\":\"进程自愿中断(如I/O完成)次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"nonvoluntary\\\"}[5m]) or irate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"nonvoluntary\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top Processes by Non-Voluntary Context Switches\",\"description\":\"进程被迫中断(如CPU时间耗尽)次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "File Descriptors", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(max_over_time(namedprocess_namegroup_open_filedesc{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_open_filedesc{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Open File Descriptors\",\"description\":\"打开文件数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( max_over_time(namedprocess_namegroup_worst_fd_ratio{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_worst_fd_ratio{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) ))*100\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by File Descriptor Usage Percent\",\"description\":\"已打开文件数与允许打开文件数占比,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Page Faults", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_major_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_major_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Major Page Faults\",\"description\":\"主要页缺失次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_minor_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_minor_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Minor Page Faults\",\"description\":\"次要页缺失次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Statuses", - "weight": 7, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Running\\\"}[5m]) or max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Running\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top running processes\",\"description\":\"运行态同名进程数量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Waiting\\\"}[5m]) or max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Waiting\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top of processes waiting on IO\",\"description\":\"等待IO状态同名进程数量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Kernel Waits(WCHAN)", - "weight": 8, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,sum(avg_over_time(namedprocess_namegroup_threads_wchan{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\"}[5m])) by (wchan) )\",\"legend\":\"{{wchan}}\"}],\"name\":\"Kernel waits for All\",\"description\":\"内核函数等待线程数量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,sum(avg_over_time(namedprocess_namegroup_threads_wchan{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\"}[5m])) by (wchan,groupname) )\",\"legend\":\"{{wchan}}\"}],\"name\":\"Kernel wait Details for All\",\"description\":\"内核函数等待线程数量按进程统计,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Uptime", - "weight": 9, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"time()-(namedprocess_namegroup_oldest_start_time_seconds{instance=~\\\"$instance\\\",groupname=~\\\"$processes\\\"}>0)\",\"legend\":\"{{groupname}}\"}],\"name\":\"Processes by uptime\",\"description\":\"进程启动时长\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"seriesToRows\"},\"options\":{\"standardOptions\":{\"util\":\"seconds\"}},\"overrides\":[{\"properties\":{\"standardOptions\":{\"util\":\"seconds\"},\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#f91010\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#21f312\"}}]}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/rabbitmq_by_categraf.json b/docker/n9eetc/dashboards/rabbitmq_by_categraf.json deleted file mode 100644 index fc384837606485f750fa2f74f3ca8371627ddd58..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/rabbitmq_by_categraf.json +++ /dev/null @@ -1,221 +0,0 @@ -[ - { - "name": "RabbitMQ 3.8+", - "tags": "", - "configs": "{\"var\":[{\"definition\":\"label_values(rabbitmq_identity_info, rabbitmq_cluster)\",\"name\":\"rabbitmq_cluster\"}]}", - "chart_groups": [ - { - "name": "Overview", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_ready * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Ready messages\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10000},\"result\":{\"color\":\"#4a90e2\"}},{\"type\":\"range\",\"match\":{\"from\":100000},\"result\":{\"color\":\"#f50a0a\"}},{\"type\":\"range\",\"match\":{\"to\":9999},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":7,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Incoming messages / s\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":50},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":5,\"x\":7,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_channels * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) - sum(rabbitmq_channel_consumers * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Publishers\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_connections * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Connections\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queues * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Queues\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_unacked * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Unacknowledged messages\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":99},\"result\":{\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#4a90e2\"}},{\"type\":\"range\",\"match\":{\"from\":500},\"result\":{\"color\":\"#d0021b\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":7,\"x\":0,\"y\":1,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_redelivered_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_get_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_get_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Outgoing messages / s\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":50},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":5,\"x\":7,\"y\":1,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_channel_consumers * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Consumers\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":1,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_channels * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Channels\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":1,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_build_info * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Nodes\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":null,\"from\":3},\"result\":{\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"from\":8},\"result\":{\"color\":\"#e70909\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":1,\"i\":\"9\"}}", - "weight": 0 - } - ] - }, - { - "name": "Nodes", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_build_info * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"nodes\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelsOfSeriesToRows\",\"columns\":[\"rabbitmq_cluster\",\"rabbitmq_node\",\"rabbitmq_version\",\"erlang_version\"]},\"options\":{\"standardOptions\":{}},\"overrides\":[{}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":1,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"(rabbitmq_resident_memory_limit_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) -\\n(rabbitmq_process_resident_memory_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Memory available before publishers blocked\",\"description\":\"If the value is zero or less, the memory alarm will be triggered and all publishing connections across all cluster nodes will be blocked.\\n\\nThis value can temporarily go negative because the memory alarm is triggered with a slight delay.\\n\\nThe kernel's view of the amount of memory used by the node can differ from what the node itself can observe. This means that this value can be negative for a sustained period of time.\\n\\nBy default nodes use resident set size (RSS) to compute how much memory they use. This strategy can be changed (see the guides below).\\n\\n* [Alarms](https://www.rabbitmq.com/alarms.html)\\n* [Memory Alarms](https://www.rabbitmq.com/memory.html)\\n* [Reasoning About Memory Use](https://www.rabbitmq.com/memory-use.html)\\n* [Blocked Connection Notifications](https://www.rabbitmq.com/connection-blocked.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":1,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_disk_space_available_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Disk space available before publishers blocked\",\"description\":\"This metric is reported for the partition where the RabbitMQ data directory is stored.\\n\\nIf the value is zero or less, the disk alarm will be triggered and all publishing connections across all cluster nodes will be blocked.\\n\\nThis value can temporarily go negative because the free disk space alarm is triggered with a slight delay.\\n\\n* [Alarms](https://www.rabbitmq.com/alarms.html)\\n* [Disk Space Alarms](https://www.rabbitmq.com/disk-alarms.html)\\n* [Disk Space](https://www.rabbitmq.com/production-checklist.html#resource-limits-disk-space)\\n* [Persistence Configuration](https://www.rabbitmq.com/persistence-conf.html)\\n* [Blocked Connection Notifications](https://www.rabbitmq.com/connection-blocked.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"(rabbitmq_process_max_fds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) -\\n(rabbitmq_process_open_fds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"File descriptors available\",\"description\":\"When this value reaches zero, new connections will not be accepted and disk write operations may fail.\\n\\nClient libraries, peer nodes and CLI tools will not be able to connect when the node runs out of available file descriptors.\\n\\n* [Open File Handles Limit](https://www.rabbitmq.com/production-checklist.html#resource-limits-file-handle-limit)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":8,\"x\":16,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"(rabbitmq_process_max_tcp_sockets * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) -\\n(rabbitmq_process_open_tcp_sockets * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"TCP sockets available\",\"description\":\"When this value reaches zero, new connections will not be accepted.\\n\\nClient libraries, peer nodes and CLI tools will not be able to connect when the node runs out of available file descriptors.\\n\\n* [Networking and RabbitMQ](https://www.rabbitmq.com/networking.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "QUEUED MESSAGES", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_ready * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages ready to be delivered to consumers\",\"description\":\"Total number of ready messages ready to be delivered to consumers.\\n\\nAim to keep this value as low as possible. RabbitMQ behaves best when messages are flowing through it. It's OK for publishers to occasionally outpace consumers, but the expectation is that consumers will eventually process all ready messages.\\n\\nIf this metric keeps increasing, your system will eventually run out of memory and/or disk space. Consider using TTL or Queue Length Limit to prevent unbounded message growth.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\\n* [Queue Length Limit](https://www.rabbitmq.com/maxlength.html)\\n* [Time-To-Live and Expiration](https://www.rabbitmq.com/ttl.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_unacked * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages pending consumer acknowledgement\",\"description\":\"The total number of messages that are either in-flight to consumers, currently being processed by consumers or simply waiting for the consumer acknowledgements to be processed by the queue. Until the queue processes the message acknowledgement, the message will remain unacknowledged.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\\n* [Confirms and Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "INCOMING MESSAGES", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages published / s\",\"description\":\"The incoming message rate before any routing rules are applied.\\n\\nIf this value is lower than the number of messages published to queues, it may indicate that some messages are delivered to more than one queue.\\n\\nIf this value is higher than the number of messages published to queues, messages cannot be routed and will either be dropped or returned to publishers.\\n\\n* [Publishers](https://www.rabbitmq.com/publishers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_confirmed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages confirmed to publishers / s\",\"description\":\"The rate of messages confirmed by the broker to publishers. Publishers must opt-in to receive message confirmations.\\n\\nIf this metric is consistently at zero it may suggest that publisher confirms are not used by clients. The safety of published messages is likely to be at risk.\\n\\n* [Publisher Confirms](https://www.rabbitmq.com/confirms.html#publisher-confirms)\\n* [Publisher Confirms and Data Safety](https://www.rabbitmq.com/publishers.html#data-safety)\\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queue_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages routed to queues / s\",\"description\":\"The rate of messages received from publishers and successfully routed to the master queue replicas.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\\n* [Publishers](https://www.rabbitmq.com/publishers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_unconfirmed[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages unconfirmed to publishers / s\",\"description\":\"The rate of messages received from publishers that have publisher confirms enabled and the broker has not confirmed yet.\\n\\n* [Publishers](https://www.rabbitmq.com/publishers.html)\\n* [Confirms and Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_unroutable_dropped_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Unroutable messages dropped / s\",\"description\":\"The rate of messages that cannot be routed and are dropped. \\n\\nAny value above zero means message loss and likely suggests a routing problem on the publisher end.\\n\\n* [Unroutable Message Handling](https://www.rabbitmq.com/publishers.html#unroutable)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_unroutable_returned_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Unroutable messages returned to publishers / s\",\"description\":\"The rate of messages that cannot be routed and are returned back to publishers.\\n\\nSustained values above zero may indicate a routing problem on the publisher end.\\n\\n* [Unroutable Message Handling](https://www.rabbitmq.com/publishers.html#unroutable)\\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - } - ] - }, - { - "name": "OUTGOING MESSAGES", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(\\n (rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\n (rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\\n) by(rabbitmq_node)\"}],\"name\":\"Messages delivered / s\",\"description\":\"The rate of messages delivered to consumers. It includes messages that have been redelivered.\\n\\nThis metric does not include messages that have been fetched by consumers using `basic.get` (consumed by polling).\\n\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_redelivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages redelivered / s\",\"description\":\"The rate of messages that have been redelivered to consumers. It includes messages that have been requeued automatically and redelivered due to channel exceptions or connection closures.\\n\\nHaving some redeliveries is expected, but if this metric is consistently non-zero, it is worth investigating why.\\n\\n* [Negative Acknowledgement and Requeuing of Deliveries](https://www.rabbitmq.com/confirms.html#consumer-nacks-requeue)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages delivered with manual ack / s\",\"description\":\"The rate of message deliveries to consumers that use manual acknowledgement mode.\\n\\nWhen this mode is used, RabbitMQ waits for consumers to acknowledge messages before more messages can be delivered.\\n\\nThis is the safest way of consuming messages.\\n\\n* [Consumer Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages delivered auto ack / s\",\"description\":\"The rate of message deliveries to consumers that use automatic acknowledgement mode.\\n\\nWhen this mode is used, RabbitMQ does not wait for consumers to acknowledge message deliveries.\\n\\nThis mode is fire-and-forget and does not offer any delivery safety guarantees. It tends to provide higher throughput and it may lead to consumer overload and higher consumer memory usage.\\n\\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_acked_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages acknowledged / s\",\"description\":\"The rate of message acknowledgements coming from consumers that use manual acknowledgement mode.\\n\\n* [Consumer Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_get_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Polling operations with auto ack / s\",\"description\":\"The rate of messages delivered to polling consumers that use automatic acknowledgement mode.\\n\\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\\n\\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_get_empty_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Polling operations that yield no result / s\",\"description\":\"The rate of polling consumer operations that yield no result.\\n\\nAny value above zero means that RabbitMQ resources are wasted by polling consumers.\\n\\nCompare this metric to the other polling consumer metrics to see the inefficiency rate.\\n\\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\\n\\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":3,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_get_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Polling operations with manual ack / s\",\"description\":\"The rate of messages delivered to polling consumers that use manual acknowledgement mode.\\n\\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\\n\\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":3,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "QUEUES", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_queues * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Total queues\",\"description\":\"Total number of queue masters per node. \\n\\nThis metric makes it easy to see sub-optimal queue distribution in a cluster.\\n\\n* [Queue Masters, Data Locality](https://www.rabbitmq.com/ha.html#master-migration-data-locality)\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queues_declared_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Queues declared / s\",\"description\":\"The rate of queue declarations performed by clients.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queues_created_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Queues created / s\",\"description\":\"The rate of new queues created (as opposed to redeclarations).\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queues_deleted_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Queues deleted / s\",\"description\":\"The rate of queues deleted.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "CHANNELS", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_channels * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Total channels\",\"description\":\"Total number of channels on all currently opened connections.\\n\\nIf this metric grows monotonically it is highly likely a channel leak in one of the applications. Confirm channel leaks by using the _Channels opened_ and _Channels closed_ metrics.\\n\\n* [Channel Leak](https://www.rabbitmq.com/channels.html#channel-leaks)\\n* [Channels](https://www.rabbitmq.com/channels.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channels_opened_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Channels opened / s\",\"description\":\"The rate of new channels opened by applications across all connections. Channels are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of channel churn or mass connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [High Channel Churn](https://www.rabbitmq.com/channels.html#high-channel-churn)\\n* [Channels](https://www.rabbitmq.com/channels.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channels_closed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Channels closed / s\",\"description\":\"The rate of channels closed by applications across all connections. Channels are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of channel churn or mass connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [High Channel Churn](https://www.rabbitmq.com/channels.html#high-channel-churn)\\n* [Channels](https://www.rabbitmq.com/channels.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "CONNECTIONS", - "weight": 7, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_connections_closed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Connections closed / s\",\"description\":\"The rate of connections closed. Connections are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of connection churn or mass connection recovery.\\n\\n* [Connections](https://www.rabbitmq.com/connections.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_connections * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Total connections\",\"description\":\"Total number of client connections.\\n\\nIf this metric grows monotonically it is highly likely a connection leak in one of the applications. Confirm connection leaks by using the _Connections opened_ and _Connections closed_ metrics.\\n\\n* [Connection Leak](https://www.rabbitmq.com/connections.html#monitoring)\\n* [Connections](https://www.rabbitmq.com/connections.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_connections_opened_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Connections opened / s\",\"description\":\"The rate of new connections opened by clients. Connections are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of connection churn or mass connection recovery.\\n\\n* [Connection Leak](https://www.rabbitmq.com/connections.html#monitoring)\\n* [Connections](https://www.rabbitmq.com/connections.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/redis_by_categraf.json b/docker/n9eetc/dashboards/redis_by_categraf.json deleted file mode 100644 index 68dee4d3e622e685fab8950902926f0e6c12f10c..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/redis_by_categraf.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "name": "Redis Overview - 模板", - "tags": "Redis Prometheus", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(redis_uptime_in_seconds,instance)\",\"selected\":\"10.206.0.16:6379\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(redis_uptime_in_seconds{instance=~\\\"$instance\\\"})\"}],\"name\":\"Redis Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_connected_clients{instance=~\\\"$instance\\\"})\"}],\"name\":\"Connected Clients\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_used_memory{instance=~\\\"$instance\\\"}\"}],\"name\":\"Memory Used\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":128000000},\"result\":{\"color\":\"#079e05\"}},{\"type\":\"range\",\"match\":{\"from\":128000000},\"result\":{\"color\":\"#f10909\"}}],\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_maxmemory{instance=~\\\"$instance\\\"}\"}],\"name\":\"Max Memory Limit\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(redis_total_commands_processed{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Commands Executed / sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(redis_keyspace_hits{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"hits\"},{\"expr\":\"irate(redis_keyspace_misses{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"misses\"}],\"name\":\"Hits / Misses per Sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"topk(5, irate(redis_cmdstat_calls{instance=~\\\"$instance\\\"} [1m]))\",\"legend\":\"{{command}}\"}],\"name\":\"Top Commands\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Keys", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum (redis_keyspace_keys{instance=~\\\"$instance\\\"}) by (db)\",\"legend\":\"{{db}}\"}],\"name\":\"Total Items per DB\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_expired_keys{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"expired\"},{\"expr\":\"sum(rate(redis_evicted_keys{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"evicted\"}],\"name\":\"Expired / Evicted\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_keyspace_keys{instance=~\\\"$instance\\\"}) - sum(redis_keyspace_expires{instance=~\\\"$instance\\\"}) \",\"legend\":\"not expiring\"},{\"expr\":\"sum(redis_keyspace_expires{instance=~\\\"$instance\\\"}) \",\"legend\":\"expiring\"}],\"name\":\"Expiring vs Not-Expiring Keys\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_total_net_input_bytes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"input\"},{\"expr\":\"sum(rate(redis_total_net_output_bytes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"output\"}],\"name\":\"Network I/O\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/redis_by_exporter.json b/docker/n9eetc/dashboards/redis_by_exporter.json deleted file mode 100644 index 8e809019305d32d18983f5c0d4b5fb137783ef2a..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/redis_by_exporter.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "name": "Redis Overview - 模板", - "tags": "Redis Prometheus", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(redis_uptime_in_seconds,instance)\",\"selected\":\"10.206.0.16:6379\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(redis_uptime_in_seconds{instance=~\\\"$instance\\\"})\"}],\"name\":\"Redis Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_connected_clients{instance=~\\\"$instance\\\"})\"}],\"name\":\"Connected Clients\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_memory_used_bytes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Memory Used\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":128000000},\"result\":{\"color\":\"#079e05\"}},{\"type\":\"range\",\"match\":{\"from\":128000000},\"result\":{\"color\":\"#f10909\"}}],\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_memory_max_bytes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Max Memory Limit\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(redis_commands_processed_total{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Commands Executed / sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(redis_keyspace_hits_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"hits\"},{\"expr\":\"irate(redis_keyspace_misses_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"misses\"}],\"name\":\"Hits / Misses per Sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"topk(5, irate(redis_commands_total{instance=~\\\"$instance\\\"} [1m]))\",\"legend\":\"{{cmd}}\"}],\"name\":\"Top Commands\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Keys", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum (redis_db_keys{instance=~\\\"$instance\\\"}) by (db)\",\"legend\":\"{{db}}\"}],\"name\":\"Total Items per DB\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_expired_keys_total{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"expired\"},{\"expr\":\"sum(rate(redis_evicted_keys_total{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"evicted\"}],\"name\":\"Expired / Evicted\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_db_keys{instance=~\\\"$instance\\\"}) - sum(redis_db_keys_expiring{instance=~\\\"$instance\\\"}) \",\"legend\":\"not expiring\"},{\"expr\":\"sum(redis_db_keys_expiring{instance=~\\\"$instance\\\"}) \",\"legend\":\"expiring\"}],\"name\":\"Expiring vs Not-Expiring Keys\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_net_input_bytes_total{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"input\"},{\"expr\":\"sum(rate(redis_net_output_bytes_total{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"output\"}],\"name\":\"Network I/O\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/tomcat_by_categraf.json b/docker/n9eetc/dashboards/tomcat_by_categraf.json deleted file mode 100644 index be18e93dbbe898b2212cbb17b0cd28459c622eb5..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/tomcat_by_categraf.json +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "name": "Tomcat - 模板", - "tags": "Categraf", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(tomcat_up, instance)\"}],\"links\":[{\"title\":\"n9e\",\"url\":\"https://n9e.gitee.io/\",\"targetBlank\":true}]}", - "chart_groups": [ - { - "name": "connector", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(tomcat_connector_bytes_sent{instance=\\\"$instance\\\"}[1m])\",\"legend\":\"sent\"},{\"expr\":\"rate(tomcat_connector_bytes_received{instance=\\\"$instance\\\"}[1m])\",\"refId\":\"B\",\"legend\":\"received\"}],\"name\":\"Traffic Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(tomcat_connector_request_count{instance=\\\"$instance\\\"}[1m])\",\"legend\":\"tomcat_connector_request_count\"},{\"expr\":\"rate(tomcat_connector_error_count{instance=\\\"$instance\\\"}[1m])\",\"refId\":\"B\",\"legend\":\"tomcat_connector_error_count\"}],\"name\":\"Request count / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_connector_max_threads{instance=\\\"$instance\\\"}\",\"legend\":\"max_threads\"},{\"expr\":\"tomcat_connector_current_thread_count{instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"current_thread_count\"},{\"expr\":\"tomcat_connector_current_threads_busy{instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"current_threads_busy\"}],\"name\":\"Tread\",\"description\":\"max_threads: The maximum number of allowed worker threads.\\ncurrent_thread_count: The number of threads managed by the thread pool\\ncurrent_threads_busy: The number of threads that are in use\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(tomcat_connector_processing_time{instance=\\\"$instance\\\"}[1m])\",\"legend\":\"{{name}}-processing_time\"}],\"name\":\"Processing time\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "mem used", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memory_max{instance=\\\"$instance\\\"}\",\"legend\":\"max\"},{\"expr\":\"tomcat_jvm_memory_total{instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"used\"},{\"expr\":\"tomcat_jvm_memory_free{instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"free\"}],\"name\":\"Mem Used\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "memorypool", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_used{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Used\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_max{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Max\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_committed{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Committed\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_init{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Init\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/windows_by_exporter.json b/docker/n9eetc/dashboards/windows_by_exporter.json deleted file mode 100644 index df60a95bcda14a7974e7d1fa006b14cf153839ee..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/windows_by_exporter.json +++ /dev/null @@ -1,99 +0,0 @@ -[ - { - "name": "Windows - 模板", - "tags": "Windows Prometheus", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(windows_system_system_up_time, instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"time() - windows_system_system_up_time{instance=~\\\"$instance\\\"}\",\"legend\":\"\"}],\"name\":\"Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_cs_logical_processors{instance=~\\\"$instance\\\"}\",\"legend\":\"\"}],\"name\":\"CPU Core Total\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_cs_physical_memory_bytes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Memory Total\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_os_processes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Process Total\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":100},\"result\":{\"color\":\"#109d06\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#d11010\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "CPU Memory Disk", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"100 * sum by (instance) (rate(windows_cpu_time_total{mode != 'idle'}[5m])) / count by (instance) (windows_cpu_core_frequency_mhz) \",\"legend\":\"CPU Util\"}],\"name\":\"Cpu Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"100 - (windows_os_physical_memory_free_bytes{instance=~\\\"$instance\\\"} / windows_cs_physical_memory_bytes{instance=~\\\"$instance\\\"})*100\"}],\"name\":\"Memory Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{\"steps\":[{\"value\":70,\"color\":\"#e71313\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"100 - (windows_logical_disk_free_bytes{instance=~\\\"$instance\\\"} / windows_logical_disk_size_bytes{instance=~\\\"$instance\\\"})*100\",\"legend\":\"{{volume}}\"}],\"name\":\"Disk Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_logical_disk_free_bytes{instance=~\\\"$instance\\\"}\",\"legend\":\"{{volume}} Free\"},{\"expr\":\"windows_logical_disk_size_bytes{instance=~\\\"$instance\\\"}\",\"legend\":\"{{volume}} Total\"}],\"name\":\"Disk Free\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Disk IO", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_logical_disk_read_bytes_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Read\"},{\"expr\":\"irate(windows_logical_disk_write_bytes_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Write\"}],\"name\":\"Read/Write Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_logical_disk_reads_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Read\"},{\"expr\":\"irate(windows_logical_disk_writes_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Write\"}],\"name\":\"Read/Write / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_net_bytes_sent_total{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}[5m])*8\",\"legend\":\"{{nic}} Sent\"},{\"expr\":\"irate(windows_net_bytes_received_total{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}[5m])*8\",\"legend\":\"{{nic}} Received\"}],\"name\":\"Sent/Received bits / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bitsIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(irate(windows_net_bytes_total{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}[5m]) * 8 / windows_net_current_bandwidth{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}) * 100\"}],\"name\":\"Network Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_net_packets_outbound_discarded{instance=~\\\"$instance\\\", nic!~'isatap.*|VPN.*'}[5m]) + irate(windows_net_packets_outbound_errors{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"outbound\"},{\"expr\":\"irate(windows_net_packets_received_discarded{job=~\\\"$job\\\",instance=~\\\"$instance\\\", nic!~'isatap.*|VPN.*'}[5m]) + irate(windows_net_packets_received_errors{job=~\\\"$job\\\",instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"received\"}],\"name\":\"Packets / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "System", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"windows_system_threads{instance=~\\\"$instance\\\"}\"}],\"name\":\"System Threads\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_system_exception_dispatches_total{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"System exception dispatches\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/dashboards/zookeeper_by_exporter.json b/docker/n9eetc/dashboards/zookeeper_by_exporter.json deleted file mode 100644 index 842f1f1554803463233986b6c8aca2c5c69d76fe..0000000000000000000000000000000000000000 --- a/docker/n9eetc/dashboards/zookeeper_by_exporter.json +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "name": "Zookeeper - 模板", - "tags": "", - "configs": "{\"var\":[{\"name\":\"job\",\"definition\":\"label_values(zk_up,job)\"},{\"definition\":\"label_values(zk_up,instance)\",\"name\":\"instance\"}]}", - "chart_groups": [ - { - "name": "overview", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_up{job=\\\"$job\\\", instance=\\\"$instance\\\"}\",\"legend\":\"up\"}],\"name\":\"up\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":40}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"color\":\"#3d950e\",\"text\":\"UP\"}},{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"color\":\"#f01414\",\"text\":\"DOWN\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_znode_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"zk_znode_count\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_watch_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"zk_watch_count\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_ephemerals_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"zk_ephemerals_count\"}],\"name\":\"zk_ephemerals_count\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(zk_packets_sent{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{instance}}-sent\"},{\"expr\":\"rate(zk_packets_received{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{instance}}-received\"}],\"name\":\"Pakages\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":1,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_num_alive_connections{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"alive_connections\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":3,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_open_file_descriptor_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}-open\"},{\"expr\":\"zk_max_file_descriptor_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"{{instance}}-max\"}],\"name\":\"file_descriptor\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":3,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_avg_latency{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}-avg\"},{\"expr\":\"zk_min_latency{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"{{instance}}-min\"},{\"expr\":\"zk_max_latency{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"{{instance}}-max\"}],\"name\":\"latency(ms)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":3,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_outstanding_requests{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"outstanding_requests\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":3,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_approximate_data_size{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"approximate_data_size\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":1,\"i\":\"9\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/docker/n9eetc/metrics.yaml b/docker/n9eetc/metrics.yaml deleted file mode 100644 index ac97fb432718a3ae8c10c6dc5b27a66cd1361002..0000000000000000000000000000000000000000 --- a/docker/n9eetc/metrics.yaml +++ /dev/null @@ -1,494 +0,0 @@ -cpu_usage_idle: CPU空闲率(单位:%) -cpu_usage_active: CPU使用率(单位:%) -cpu_usage_system: CPU内核态时间占比(单位:%) -cpu_usage_user: CPU用户态时间占比(单位:%) -cpu_usage_nice: 低优先级用户态CPU时间占比,也就是进程nice值被调整为1-19之间的CPU时间。这里注意,nice可取值范围是-20到19,数值越大,优先级反而越低(单位:%) -cpu_usage_iowait: CPU等待I/O的时间占比(单位:%) -cpu_usage_irq: CPU处理硬中断的时间占比(单位:%) -cpu_usage_softirq: CPU处理软中断的时间占比(单位:%) -cpu_usage_steal: 在虚拟机环境下有该指标,表示CPU被其他虚拟机争用的时间占比,超过20就表示争抢严重(单位:%) -cpu_usage_guest: 通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的CPU时间占比(单位:%) -cpu_usage_guest_nice: 以低优先级运行虚拟机的时间占比(单位:%) - -disk_free: 硬盘分区剩余量(单位:byte) -disk_used: 硬盘分区使用量(单位:byte) -disk_used_percent: 硬盘分区使用率(单位:%) -disk_total: 硬盘分区总量(单位:byte) -disk_inodes_free: 硬盘分区inode剩余量 -disk_inodes_used: 硬盘分区inode使用量 -disk_inodes_total: 硬盘分区inode总量 - -diskio_io_time: 从设备视角来看I/O请求总时间,队列中有I/O请求就计数(单位:毫秒),counter类型,需要用函数求rate才有使用价值 -diskio_iops_in_progress: 已经分配给设备驱动且尚未完成的IO请求,不包含在队列中但尚未分配给设备驱动的IO请求,gauge类型 -diskio_merged_reads: 相邻读请求merge读的次数,counter类型 -diskio_merged_writes: 相邻写请求merge写的次数,counter类型 -diskio_read_bytes: 读取的byte数量,counter类型,需要用函数求rate才有使用价值 -diskio_read_time: 读请求总时间(单位:毫秒),counter类型,需要用函数求rate才有使用价值 -diskio_reads: 读请求次数,counter类型,需要用函数求rate才有使用价值 -diskio_weighted_io_time: 从I/O请求视角来看I/O等待总时间,如果同时有多个I/O请求,时间会叠加(单位:毫秒) -diskio_write_bytes: 写入的byte数量,counter类型,需要用函数求rate才有使用价值 -diskio_write_time: 写请求总时间(单位:毫秒),counter类型,需要用函数求rate才有使用价值 -diskio_writes: 写请求次数,counter类型,需要用函数求rate才有使用价值 - -kernel_boot_time: 内核启动时间 -kernel_context_switches: 内核上下文切换次数 -kernel_entropy_avail: linux系统内部的熵池 -kernel_interrupts: 内核中断次数 -kernel_processes_forked: fork的进程数 - -mem_active: 活跃使用的内存总数(包括cache和buffer内存) -mem_available: 应用程序可用内存数 -mem_available_percent: 内存剩余百分比(0~100) -mem_buffered: 用来给文件做缓冲大小 -mem_cached: 被高速缓冲存储器(cache memory)用的内存的大小(等于 diskcache minus SwapCache ) -mem_commit_limit: 根据超额分配比率('vm.overcommit_ratio'),这是当前在系统上分配可用的内存总量,这个限制只是在模式2('vm.overcommit_memory')时启用 -mem_committed_as: 目前在系统上分配的内存量。是所有进程申请的内存的总和 -mem_dirty: 等待被写回到磁盘的内存大小 -mem_free: 空闲内存数 -mem_high_free: 未被使用的高位内存大小 -mem_high_total: 高位内存总大小(Highmem是指所有内存高于860MB的物理内存,Highmem区域供用户程序使用,或用于页面缓存。该区域不是直接映射到内核空间。内核必须使用不同的手法使用该段内存) -mem_huge_page_size: 每个大页的大小 -mem_huge_pages_free: 池中尚未分配的 HugePages 数量 -mem_huge_pages_total: 预留HugePages的总个数 -mem_inactive: 空闲的内存数(包括free和avalible的内存) -mem_low_free: 未被使用的低位大小 -mem_low_total: 低位内存总大小,低位可以达到高位内存一样的作用,而且它还能够被内核用来记录一些自己的数据结构 -mem_mapped: 设备和文件等映射的大小 -mem_page_tables: 管理内存分页页面的索引表的大小 -mem_shared: 多个进程共享的内存总额 -mem_slab: 内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗 -mem_sreclaimable: 可收回Slab的大小 -mem_sunreclaim: 不可收回Slab的大小(SUnreclaim+SReclaimable=Slab) -mem_swap_cached: 被高速缓冲存储器(cache memory)用的交换空间的大小,已经被交换出来的内存,但仍然被存放在swapfile中。用来在需要的时候很快的被替换而不需要再次打开I/O端口 -mem_swap_free: 未被使用交换空间的大小 -mem_swap_total: 交换空间的总大小 -mem_total: 内存总数 -mem_used: 已用内存数 -mem_used_percent: 已用内存数百分比(0~100) -mem_vmalloc_chunk: 最大的连续未被使用的vmalloc区域 -mem_vmalloc_totalL: 可以vmalloc虚拟内存大小 -mem_vmalloc_used: vmalloc已使用的虚拟内存大小 -mem_write_back: 正在被写回到磁盘的内存大小 -mem_write_back_tmp: FUSE用于临时写回缓冲区的内存 - -net_bytes_recv: 网卡收包总数(bytes) -net_bytes_sent: 网卡发包总数(bytes) -net_drop_in: 网卡收丢包数量 -net_drop_out: 网卡发丢包数量 -net_err_in: 网卡收包错误数量 -net_err_out: 网卡发包错误数量 -net_packets_recv: 网卡收包数量 -net_packets_sent: 网卡发包数量 - -netstat_tcp_established: ESTABLISHED状态的网络链接数 -netstat_tcp_fin_wait1: FIN_WAIT1状态的网络链接数 -netstat_tcp_fin_wait2: FIN_WAIT2状态的网络链接数 -netstat_tcp_last_ack: LAST_ACK状态的网络链接数 -netstat_tcp_listen: LISTEN状态的网络链接数 -netstat_tcp_syn_recv: SYN_RECV状态的网络链接数 -netstat_tcp_syn_sent: SYN_SENT状态的网络链接数 -netstat_tcp_time_wait: TIME_WAIT状态的网络链接数 -netstat_udp_socket: UDP状态的网络链接数 - -processes_blocked: 不可中断的睡眠状态下的进程数('U','D','L') -processes_dead: 回收中的进程数('X') -processes_idle: 挂起的空闲进程数('I') -processes_paging: 分页进程数('P') -processes_running: 运行中的进程数('R') -processes_sleeping: 可中断进程数('S') -processes_stopped: 暂停状态进程数('T') -processes_total: 总进程数 -processes_total_threads: 总线程数 -processes_unknown: 未知状态进程数 -processes_zombies: 僵尸态进程数('Z') - -swap_used_percent: Swap空间换出数据量 - -system_load1: 1分钟平均load值 -system_load5: 5分钟平均load值 -system_load15: 15分钟平均load值 -system_n_users: 用户数 -system_n_cpus: CPU核数 -system_uptime: 系统启动时间 - -nginx_accepts: 自nginx启动起,与客户端建立过得连接总数 -nginx_active: 当前nginx正在处理的活动连接数,等于Reading/Writing/Waiting总和 -nginx_handled: 自nginx启动起,处理过的客户端连接总数 -nginx_reading: 正在读取HTTP请求头部的连接总数 -nginx_requests: 自nginx启动起,处理过的客户端请求总数,由于存在HTTP Krrp-Alive请求,该值会大于handled值 -nginx_upstream_check_fall: upstream_check模块检测到后端失败的次数 -nginx_upstream_check_rise: upstream_check模块对后端的检测次数 -nginx_upstream_check_status_code: 后端upstream的状态,up为1,down为0 -nginx_waiting: 开启 keep-alive 的情况下,这个值等于 active – (reading+writing), 意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留连接 -nginx_writing: 正在向客户端发送响应的连接总数 - -http_response_content_length: HTTP消息实体的传输长度 -http_response_http_response_code: http响应状态码 -http_response_response_time: http响应用时 -http_response_result_code: url探测结果0为正常否则url无法访问 - -# [mysqld_exporter] -mysql_global_status_uptime: The number of seconds that the server has been up.(Gauge) -mysql_global_status_uptime_since_flush_status: The number of seconds since the most recent FLUSH STATUS statement.(Gauge) -mysql_global_status_queries: The number of statements executed by the server. This variable includes statements executed within stored programs, unlike the Questions variable. It does not count COM_PING or COM_STATISTICS commands.(Counter) -mysql_global_status_threads_connected: The number of currently open connections.(Counter) -mysql_global_status_connections: The number of connection attempts (successful or not) to the MySQL server.(Gauge) -mysql_global_status_max_used_connections: The maximum number of connections that have been in use simultaneously since the server started.(Gauge) -mysql_global_status_threads_running: The number of threads that are not sleeping.(Gauge) -mysql_global_status_questions: The number of statements executed by the server. This includes only statements sent to the server by clients and not statements executed within stored programs, unlike the Queries variable. This variable does not count COM_PING, COM_STATISTICS, COM_STMT_PREPARE, COM_STMT_CLOSE, or COM_STMT_RESET commands.(Counter) -mysql_global_status_threads_cached: The number of threads in the thread cache.(Counter) -mysql_global_status_threads_created: The number of threads created to handle connections. If Threads_created is big, you may want to increase the thread_cache_size value. The cache miss rate can be calculated as Threads_created/Connections.(Counter) -mysql_global_status_created_tmp_tables: The number of internal temporary tables created by the server while executing statements.(Counter) -mysql_global_status_created_tmp_disk_tables: The number of internal on-disk temporary tables created by the server while executing statements. You can compare the number of internal on-disk temporary tables created to the total number of internal temporary tables created by comparing Created_tmp_disk_tables and Created_tmp_tables values.(Counter) -mysql_global_status_created_tmp_files: How many temporary files mysqld has created.(Counter) -mysql_global_status_select_full_join: The number of joins that perform table scans because they do not use indexes. If this value is not 0, you should carefully check the indexes of your tables.(Counter) -mysql_global_status_select_full_range_join: The number of joins that used a range search on a reference table.(Counter) -mysql_global_status_select_range: The number of joins that used ranges on the first table. This is normally not a critical issue even if the value is quite large.(Counter) -mysql_global_status_select_range_check: The number of joins without keys that check for key usage after each row. If this is not 0, you should carefully check the indexes of your tables.(Counter) -mysql_global_status_select_scan: The number of joins that did a full scan of the first table.(Counter) -mysql_global_status_sort_rows: The number of sorted rows.(Counter) -mysql_global_status_sort_range: The number of sorts that were done using ranges.(Counter) -mysql_global_status_sort_merge_passes: The number of merge passes that the sort algorithm has had to do. If this value is large, you should consider increasing the value of the sort_buffer_size system variable.(Counter) -mysql_global_status_sort_scan: The number of sorts that were done by scanning the table.(Counter) -mysql_global_status_slow_queries: The number of queries that have taken more than long_query_time seconds. This counter increments regardless of whether the slow query log is enabled.(Counter) -mysql_global_status_aborted_connects: The number of failed attempts to connect to the MySQL server.(Counter) -mysql_global_status_aborted_clients: The number of connections that were aborted because the client died without closing the connection properly.(Counter) -mysql_global_status_table_locks_immediate: The number of times that a request for a table lock could be granted immediately. Locks Immediate rising and falling is normal activity.(Counter) -mysql_global_status_table_locks_waited: The number of times that a request for a table lock could not be granted immediately and a wait was needed. If this is high and you have performance problems, you should first optimize your queries, and then either split your table or tables or use replication.(Counter) -mysql_global_status_bytes_received: The number of bytes received from all clients.(Counter) -mysql_global_status_bytes_sent: The number of bytes sent to all clients.(Counter) -mysql_global_status_innodb_page_size: InnoDB page size (default 16KB). Many values are counted in pages; the page size enables them to be easily converted to bytes.(Gauge) -mysql_global_status_buffer_pool_pages: The number of pages in the InnoDB buffer pool.(Gauge) -mysql_global_status_commands_total: The number of times each xxx statement has been executed.(Counter) -mysql_global_status_handlers_total: Handler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes. This is in fact the layer between the Storage Engine and MySQL.(Counter) -mysql_global_status_opened_files: The number of files that have been opened with my_open() (a mysys library function). Parts of the server that open files without using this function do not increment the count.(Counter) -mysql_global_status_open_tables: The number of tables that are open.(Gauge) -mysql_global_status_opened_tables: The number of tables that have been opened. If Opened_tables is big, your table_open_cache value is probably too small.(Counter) -mysql_global_status_table_open_cache_hits: The number of hits for open tables cache lookups.(Counter) -mysql_global_status_table_open_cache_misses: The number of misses for open tables cache lookups.(Counter) -mysql_global_status_table_open_cache_overflows: The number of overflows for the open tables cache.(Counter) -mysql_global_status_innodb_num_open_files: The number of files InnoDB currently holds open.(Gauge) -mysql_global_status_connection_errors_total: These variables provide information about errors that occur during the client connection process.(Counter) -mysql_global_status_innodb_buffer_pool_read_requests: The number of logical read requests.(Counter) -mysql_global_status_innodb_buffer_pool_reads: The number of logical reads that InnoDB could not satisfy from the buffer pool, and had to read directly from disk.(Counter) - -mysql_global_variables_thread_cache_size: How many threads the server should cache for reuse.(Gauge) -mysql_global_variables_max_connections: The maximum permitted number of simultaneous client connections.(Gauge) -mysql_global_variables_innodb_buffer_pool_size: The size in bytes of the buffer pool, the memory area where InnoDB caches table and index data. The default value is 134217728 bytes (128MB).(Gauge) -mysql_global_variables_innodb_log_buffer_size: The size in bytes of the buffer that InnoDB uses to write to the log files on disk.(Gauge) -mysql_global_variables_key_buffer_size: Index blocks for MyISAM tables are buffered and are shared by all threads.(Gauge) -mysql_global_variables_query_cache_size: The amount of memory allocated for caching query results.(Gauge) -mysql_global_variables_table_open_cache: The number of open tables for all threads.(Gauge) -mysql_global_variables_open_files_limit: The number of file descriptors available to mysqld from the operating system.(Gauge) - -# [redis_exporter] -redis_active_defrag_running: When activedefrag is enabled, this indicates whether defragmentation is currently active, and the CPU percentage it intends to utilize. -redis_allocator_active_bytes: Total bytes in the allocator active pages, this includes external-fragmentation. -redis_allocator_allocated_bytes: Total bytes allocated form the allocator, including internal-fragmentation. Normally the same as used_memory. -redis_allocator_frag_bytes: Delta between allocator_active and allocator_allocated. See note about mem_fragmentation_bytes. -redis_allocator_frag_ratio: Ratio between allocator_active and allocator_allocated. This is the true (external) fragmentation metric (not mem_fragmentation_ratio). -redis_allocator_resident_bytes: Total bytes resident (RSS) in the allocator, this includes pages that can be released to the OS (by MEMORY PURGE, or just waiting). -redis_allocator_rss_bytes: Delta between allocator_resident and allocator_active. -redis_allocator_rss_ratio: Ratio between allocator_resident and allocator_active. This usually indicates pages that the allocator can and probably will soon release back to the OS. -redis_aof_current_rewrite_duration_sec: Duration of the on-going AOF rewrite operation if any. -redis_aof_enabled: Flag indicating AOF logging is activated. -redis_aof_last_bgrewrite_status: Status of the last AOF rewrite operation. -redis_aof_last_cow_size_bytes: The size in bytes of copy-on-write memory during the last AOF rewrite operation. -redis_aof_last_rewrite_duration_sec: Duration of the last AOF rewrite operation in seconds. -redis_aof_last_write_status: Status of the last write operation to the AOF. -redis_aof_rewrite_in_progress: Flag indicating a AOF rewrite operation is on-going. -redis_aof_rewrite_scheduled: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete. -redis_blocked_clients: Number of clients pending on a blocking call (BLPOP, BRPOP, BRPOPLPUSH, BLMOVE, BZPOPMIN, BZPOPMAX). -redis_client_recent_max_input_buffer_bytes: Biggest input buffer among current client connections. -redis_client_recent_max_output_buffer_bytes: Biggest output buffer among current client connections. -redis_cluster_enabled: Indicate Redis cluster is enabled. -redis_commands_duration_seconds_total: The total CPU time consumed by these commands.(Counter) -redis_commands_processed_total: Total number of commands processed by the server.(Counter) -redis_commands_total: The number of calls that reached command execution (not rejected).(Counter) -redis_config_maxclients: The value of the maxclients configuration directive. This is the upper limit for the sum of connected_clients, connected_slaves and cluster_connections. -redis_config_maxmemory: The value of the maxmemory configuration directive. -redis_connected_clients: Number of client connections (excluding connections from replicas). -redis_connected_slaves: Number of connected replicas. -redis_connections_received_total: Total number of connections accepted by the server.(Counter) -redis_cpu_sys_children_seconds_total: System CPU consumed by the background processes.(Counter) -redis_cpu_sys_seconds_total: System CPU consumed by the Redis server, which is the sum of system CPU consumed by all threads of the server process (main thread and background threads).(Counter) -redis_cpu_user_children_seconds_total: User CPU consumed by the background processes.(Counter) -redis_cpu_user_seconds_total: User CPU consumed by the Redis server, which is the sum of user CPU consumed by all threads of the server process (main thread and background threads).(Counter) -redis_db_keys: Total number of keys by DB. -redis_db_keys_expiring: Total number of expiring keys by DB -redis_defrag_hits: Number of value reallocations performed by active the defragmentation process. -redis_defrag_misses: Number of aborted value reallocations started by the active defragmentation process. -redis_defrag_key_hits: Number of keys that were actively defragmented. -redis_defrag_key_misses: Number of keys that were skipped by the active defragmentation process. -redis_evicted_keys_total: Number of evicted keys due to maxmemory limit.(Counter) -redis_expired_keys_total: Total number of key expiration events.(Counter) -redis_expired_stale_percentage: The percentage of keys probably expired. -redis_expired_time_cap_reached_total: The count of times that active expiry cycles have stopped early. -redis_exporter_last_scrape_connect_time_seconds: The duration(in seconds) to connect when scrape. -redis_exporter_last_scrape_duration_seconds: The last scrape duration. -redis_exporter_last_scrape_error: The last scrape error status. -redis_exporter_scrape_duration_seconds_count: Durations of scrapes by the exporter -redis_exporter_scrape_duration_seconds_sum: Durations of scrapes by the exporter -redis_exporter_scrapes_total: Current total redis scrapes.(Counter) -redis_instance_info: Information about the Redis instance. -redis_keyspace_hits_total: Hits total.(Counter) -redis_keyspace_misses_total: Misses total.(Counter) -redis_last_key_groups_scrape_duration_milliseconds: Duration of the last key group metrics scrape in milliseconds. -redis_last_slow_execution_duration_seconds: The amount of time needed for last slow execution, in seconds. -redis_latest_fork_seconds: The amount of time needed for last fork, in seconds. -redis_lazyfree_pending_objects: The number of objects waiting to be freed (as a result of calling UNLINK, or FLUSHDB and FLUSHALL with the ASYNC option). -redis_master_repl_offset: The server's current replication offset. -redis_mem_clients_normal: Memory used by normal clients.(Gauge) -redis_mem_clients_slaves: Memory used by replica clients - Starting Redis 7.0, replica buffers share memory with the replication backlog, so this field can show 0 when replicas don't trigger an increase of memory usage. -redis_mem_fragmentation_bytes: Delta between used_memory_rss and used_memory. Note that when the total fragmentation bytes is low (few megabytes), a high ratio (e.g. 1.5 and above) is not an indication of an issue. -redis_mem_fragmentation_ratio: Ratio between used_memory_rss and used_memory. Note that this doesn't only includes fragmentation, but also other process overheads (see the allocator_* metrics), and also overheads like code, shared libraries, stack, etc. -redis_mem_not_counted_for_eviction_bytes: (Gauge) -redis_memory_max_bytes: Max memory limit in bytes. -redis_memory_used_bytes: Total number of bytes allocated by Redis using its allocator (either standard libc, jemalloc, or an alternative allocator such as tcmalloc) -redis_memory_used_dataset_bytes: The size in bytes of the dataset (used_memory_overhead subtracted from used_memory) -redis_memory_used_lua_bytes: Number of bytes used by the Lua engine. -redis_memory_used_overhead_bytes: The sum in bytes of all overheads that the server allocated for managing its internal data structures. -redis_memory_used_peak_bytes: Peak memory consumed by Redis (in bytes) -redis_memory_used_rss_bytes: Number of bytes that Redis allocated as seen by the operating system (a.k.a resident set size). This is the number reported by tools such as top(1) and ps(1) -redis_memory_used_scripts_bytes: Number of bytes used by cached Lua scripts -redis_memory_used_startup_bytes: Initial amount of memory consumed by Redis at startup in bytes -redis_migrate_cached_sockets_total: The number of sockets open for MIGRATE purposes -redis_net_input_bytes_total: Total input bytes(Counter) -redis_net_output_bytes_total: Total output bytes(Counter) -redis_process_id: Process ID -redis_pubsub_channels: Global number of pub/sub channels with client subscriptions -redis_pubsub_patterns: Global number of pub/sub pattern with client subscriptions -redis_rdb_bgsave_in_progress: Flag indicating a RDB save is on-going -redis_rdb_changes_since_last_save: Number of changes since the last dump -redis_rdb_current_bgsave_duration_sec: Duration of the on-going RDB save operation if any -redis_rdb_last_bgsave_duration_sec: Duration of the last RDB save operation in seconds -redis_rdb_last_bgsave_status: Status of the last RDB save operation -redis_rdb_last_cow_size_bytes: The size in bytes of copy-on-write memory during the last RDB save operation -redis_rdb_last_save_timestamp_seconds: Epoch-based timestamp of last successful RDB save -redis_rejected_connections_total: Number of connections rejected because of maxclients limit(Counter) -redis_repl_backlog_first_byte_offset: The master offset of the replication backlog buffer -redis_repl_backlog_history_bytes: Size in bytes of the data in the replication backlog buffer -redis_repl_backlog_is_active: Flag indicating replication backlog is active -redis_replica_partial_resync_accepted: The number of accepted partial resync requests(Gauge) -redis_replica_partial_resync_denied: The number of denied partial resync requests(Gauge) -redis_replica_resyncs_full: The number of full resyncs with replicas -redis_replication_backlog_bytes: Memory used by replication backlog -redis_second_repl_offset: The offset up to which replication IDs are accepted. -redis_slave_expires_tracked_keys: The number of keys tracked for expiry purposes (applicable only to writable replicas)(Gauge) -redis_slowlog_last_id: Last id of slowlog -redis_slowlog_length: Total slowlog -redis_start_time_seconds: Start time of the Redis instance since unix epoch in seconds. -redis_target_scrape_request_errors_total: Errors in requests to the exporter -redis_up: Flag indicating redis instance is up -redis_uptime_in_seconds: Number of seconds since Redis server start - -# [windows_exporter] -windows_cpu_clock_interrupts_total: Total number of received and serviced clock tick interrupts(counter) -windows_cpu_core_frequency_mhz: Core frequency in megahertz(gauge) -windows_cpu_cstate_seconds_total: Time spent in low-power idle state(counter) -windows_cpu_dpcs_total: Total number of received and serviced deferred procedure calls (DPCs)(counter) -windows_cpu_idle_break_events_total: Total number of time processor was woken from idle(counter) -windows_cpu_interrupts_total: Total number of received and serviced hardware interrupts(counter) -windows_cpu_parking_status: Parking Status represents whether a processor is parked or not(gauge) -windows_cpu_processor_performance: Processor Performance is the average performance of the processor while it is executing instructions, as a percentage of the nominal performance of the processor. On some processors, Processor Performance may exceed 100%(gauge) -windows_cpu_time_total: Time that processor spent in different modes (idle, user, system, ...)(counter) -windows_cs_hostname: Labeled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain(gauge) -windows_cs_logical_processors: ComputerSystem.NumberOfLogicalProcessors(gauge) -windows_cs_physical_memory_bytes: ComputerSystem.TotalPhysicalMemory(gauge) -windows_exporter_build_info: A metric with a constant '1' value labeled by version, revision, branch, and goversion from which windows_exporter was built.(gauge) -windows_exporter_collector_duration_seconds: Duration of a collection.(gauge) -windows_exporter_collector_success: Whether the collector was successful.(gauge) -windows_exporter_collector_timeout: Whether the collector timed out.(gauge) -windows_exporter_perflib_snapshot_duration_seconds: Duration of perflib snapshot capture(gauge) -windows_logical_disk_free_bytes: Free space in bytes (LogicalDisk.PercentFreeSpace)(gauge) -windows_logical_disk_idle_seconds_total: Seconds that the disk was idle (LogicalDisk.PercentIdleTime)(counter) -windows_logical_disk_read_bytes_total: The number of bytes transferred from the disk during read operations (LogicalDisk.DiskReadBytesPerSec)(counter) -windows_logical_disk_read_latency_seconds_total: Shows the average time, in seconds, of a read operation from the disk (LogicalDisk.AvgDiskSecPerRead)(counter) -windows_logical_disk_read_seconds_total: Seconds that the disk was busy servicing read requests (LogicalDisk.PercentDiskReadTime)(counter) -windows_logical_disk_read_write_latency_seconds_total: Shows the time, in seconds, of the average disk transfer (LogicalDisk.AvgDiskSecPerTransfer)(counter) -windows_logical_disk_reads_total: The number of read operations on the disk (LogicalDisk.DiskReadsPerSec)(counter) -windows_logical_disk_requests_queued: The number of requests queued to the disk (LogicalDisk.CurrentDiskQueueLength)(gauge) -windows_logical_disk_size_bytes: Total space in bytes (LogicalDisk.PercentFreeSpace_Base)(gauge) -windows_logical_disk_split_ios_total: The number of I/Os to the disk were split into multiple I/Os (LogicalDisk.SplitIOPerSec)(counter) -windows_logical_disk_write_bytes_total: The number of bytes transferred to the disk during write operations (LogicalDisk.DiskWriteBytesPerSec)(counter) -windows_logical_disk_write_latency_seconds_total: Shows the average time, in seconds, of a write operation to the disk (LogicalDisk.AvgDiskSecPerWrite)(counter) -windows_logical_disk_write_seconds_total: Seconds that the disk was busy servicing write requests (LogicalDisk.PercentDiskWriteTime)(counter) -windows_logical_disk_writes_total: The number of write operations on the disk (LogicalDisk.DiskWritesPerSec)(counter) -windows_net_bytes_received_total: (Network.BytesReceivedPerSec)(counter) -windows_net_bytes_sent_total: (Network.BytesSentPerSec)(counter) -windows_net_bytes_total: (Network.BytesTotalPerSec)(counter) -windows_net_current_bandwidth: (Network.CurrentBandwidth)(gauge) -windows_net_packets_outbound_discarded_total: (Network.PacketsOutboundDiscarded)(counter) -windows_net_packets_outbound_errors_total: (Network.PacketsOutboundErrors)(counter) -windows_net_packets_received_discarded_total: (Network.PacketsReceivedDiscarded)(counter) -windows_net_packets_received_errors_total: (Network.PacketsReceivedErrors)(counter) -windows_net_packets_received_total: (Network.PacketsReceivedPerSec)(counter) -windows_net_packets_received_unknown_total: (Network.PacketsReceivedUnknown)(counter) -windows_net_packets_sent_total: (Network.PacketsSentPerSec)(counter) -windows_net_packets_total: (Network.PacketsPerSec)(counter) -windows_os_info: OperatingSystem.Caption, OperatingSystem.Version(gauge) -windows_os_paging_free_bytes: OperatingSystem.FreeSpaceInPagingFiles(gauge) -windows_os_paging_limit_bytes: OperatingSystem.SizeStoredInPagingFiles(gauge) -windows_os_physical_memory_free_bytes: OperatingSystem.FreePhysicalMemory(gauge) -windows_os_process_memory_limix_bytes: OperatingSystem.MaxProcessMemorySize(gauge) -windows_os_processes: OperatingSystem.NumberOfProcesses(gauge) -windows_os_processes_limit: OperatingSystem.MaxNumberOfProcesses(gauge) -windows_os_time: OperatingSystem.LocalDateTime(gauge) -windows_os_timezone: OperatingSystem.LocalDateTime(gauge) -windows_os_users: OperatingSystem.NumberOfUsers(gauge) -windows_os_virtual_memory_bytes: OperatingSystem.TotalVirtualMemorySize(gauge) -windows_os_virtual_memory_free_bytes: OperatingSystem.FreeVirtualMemory(gauge) -windows_os_visible_memory_bytes: OperatingSystem.TotalVisibleMemorySize(gauge) -windows_service_info: A metric with a constant '1' value labeled with service information(gauge) -windows_service_start_mode: The start mode of the service (StartMode)(gauge) -windows_service_state: The state of the service (State)(gauge) -windows_service_status: The status of the service (Status)(gauge) -windows_system_context_switches_total: Total number of context switches (WMI source is PerfOS_System.ContextSwitchesPersec)(counter) -windows_system_exception_dispatches_total: Total number of exceptions dispatched (WMI source is PerfOS_System.ExceptionDispatchesPersec)(counter) -windows_system_processor_queue_length: Length of processor queue (WMI source is PerfOS_System.ProcessorQueueLength)(gauge) -windows_system_system_calls_total: Total number of system calls (WMI source is PerfOS_System.SystemCallsPersec)(counter) -windows_system_system_up_time: System boot time (WMI source is PerfOS_System.SystemUpTime)(gauge) -windows_system_threads: Current number of threads (WMI source is PerfOS_System.Threads)(gauge) - -# [node_exporter] -# SYSTEM -# CPU context switch 次数 -node_context_switches_total: context_switches -# Interrupts 次数 -node_intr_total: Interrupts -# 运行的进程数 -node_procs_running: Processes in runnable state -# 熵池大小 -node_entropy_available_bits: Entropy available to random number generators -node_time_seconds: System time in seconds since epoch (1970) -node_boot_time_seconds: Node boot time, in unixtime -# CPU -node_cpu_seconds_total: Seconds the CPUs spent in each mode -node_load1: cpu load 1m -node_load5: cpu load 5m -node_load15: cpu load 15m - -# MEM -# 内核态 -# 用户追踪已从交换区获取但尚未修改的页面的内存 -node_memory_SwapCached_bytes: Memory that keeps track of pages that have been fetched from swap but not yet been modified -# 内核用于缓存数据结构供自己使用的内存 -node_memory_Slab_bytes: Memory used by the kernel to cache data structures for its own use -# slab中可回收的部分 -node_memory_SReclaimable_bytes: SReclaimable - Part of Slab, that might be reclaimed, such as caches -# slab中不可回收的部分 -node_memory_SUnreclaim_bytes: Part of Slab, that cannot be reclaimed on memory pressure -# Vmalloc内存区的大小 -node_memory_VmallocTotal_bytes: Total size of vmalloc memory area -# vmalloc已分配的内存,虚拟地址空间上的连续的内存 -node_memory_VmallocUsed_bytes: Amount of vmalloc area which is used -# vmalloc区可用的连续最大快的大小,通过此指标可以知道vmalloc可分配连续内存的最大值 -node_memory_VmallocChunk_bytes: Largest contigious block of vmalloc area which is free -# 内存的硬件故障删除掉的内存页的总大小 -node_memory_HardwareCorrupted_bytes: Amount of RAM that the kernel identified as corrupted / not working -# 用于在虚拟和物理内存地址之间映射的内存 -node_memory_PageTables_bytes: Memory used to map between virtual and physical memory addresses (gauge) -# 内核栈内存,常驻内存,不可回收 -node_memory_KernelStack_bytes: Kernel memory stack. This is not reclaimable -# 用来访问高端内存,复制高端内存的临时buffer,称为“bounce buffering”,会降低I/O 性能 -node_memory_Bounce_bytes: Memory used for block device bounce buffers -#用户态 -# 单个巨页大小 -node_memory_Hugepagesize_bytes: Huge Page size -# 系统分配的常驻巨页数 -node_memory_HugePages_Total: Total size of the pool of huge pages -# 系统空闲的巨页数 -node_memory_HugePages_Free: Huge pages in the pool that are not yet allocated -# 进程已申请但未使用的巨页数 -node_memory_HugePages_Rsvd: Huge pages for which a commitment to allocate from the pool has been made, but no allocation -# 超过系统设定的常驻HugePages数量的个数 -node_memory_HugePages_Surp: Huge pages in the pool above the value in /proc/sys/vm/nr_hugepages -# 透明巨页 Transparent HugePages (THP) -node_memory_AnonHugePages_bytes: Memory in anonymous huge pages -# inactivelist中的File-backed内存 -node_memory_Inactive_file_bytes: File-backed memory on inactive LRU list -# inactivelist中的Anonymous内存 -node_memory_Inactive_anon_bytes: Anonymous and swap cache on inactive LRU list, including tmpfs (shmem) -# activelist中的File-backed内存 -node_memory_Active_file_bytes: File-backed memory on active LRU list -# activelist中的Anonymous内存 -node_memory_Active_anon_bytes: Anonymous and swap cache on active least-recently-used (LRU) list, including tmpfs -# 禁止换出的页,对应 Unevictable 链表 -node_memory_Unevictable_bytes: Amount of unevictable memory that can't be swapped out for a variety of reasons -# 共享内存 -node_memory_Shmem_bytes: Used shared memory (shared between several processes, thus including RAM disks) -# 匿名页内存大小 -node_memory_AnonPages_bytes: Memory in user pages not backed by files -# 被关联的内存页大小 -node_memory_Mapped_bytes: Used memory in mapped pages files which have been mmaped, such as libraries -# file-backed内存页缓存大小 -node_memory_Cached_bytes: Parked file data (file content) cache -# 系统中有多少匿名页曾经被swap-out、现在又被swap-in并且swap-in之后页面中的内容一直没发生变化 -node_memory_SwapCached_bytes: Memory that keeps track of pages that have been fetched from swap but not yet been modified -# 被mlock()系统调用锁定的内存大小 -node_memory_Mlocked_bytes: Size of pages locked to memory using the mlock() system call -# 块设备(block device)所占用的缓存页 -node_memory_Buffers_bytes: Block device (e.g. harddisk) cache -node_memory_SwapTotal_bytes: Memory information field SwapTotal_bytes -node_memory_SwapFree_bytes: Memory information field SwapFree_bytes - -# DISK -node_filesystem_files_free: Filesystem space available to non-root users in byte -node_filesystem_free_bytes: Filesystem free space in bytes -node_filesystem_size_bytes: Filesystem size in bytes -node_filesystem_files_free: Filesystem total free file nodes -node_filesystem_files: Filesystem total free file nodes -node_filefd_maximum: Max open files -node_filefd_allocated: Open files -node_filesystem_readonly: Filesystem read-only status -node_filesystem_device_error: Whether an error occurred while getting statistics for the given device -node_disk_reads_completed_total: The total number of reads completed successfully -node_disk_writes_completed_total: The total number of writes completed successfully -node_disk_reads_merged_total: The number of reads merged -node_disk_writes_merged_total: The number of writes merged -node_disk_read_bytes_total: The total number of bytes read successfully -node_disk_written_bytes_total: The total number of bytes written successfully -node_disk_io_time_seconds_total: Total seconds spent doing I/Os -node_disk_read_time_seconds_total: The total number of seconds spent by all reads -node_disk_write_time_seconds_total: The total number of seconds spent by all writes -node_disk_io_time_weighted_seconds_total: The weighted of seconds spent doing I/Os - -# NET -node_network_receive_bytes_total: Network device statistic receive_bytes (counter) -node_network_transmit_bytes_total: Network device statistic transmit_bytes (counter) -node_network_receive_packets_total: Network device statistic receive_bytes -node_network_transmit_packets_total: Network device statistic transmit_bytes -node_network_receive_errs_total: Network device statistic receive_errs -node_network_transmit_errs_total: Network device statistic transmit_errs -node_network_receive_drop_total: Network device statistic receive_drop -node_network_transmit_drop_total: Network device statistic transmit_drop -node_nf_conntrack_entries: Number of currently allocated flow entries for connection tracking -node_sockstat_TCP_alloc: Number of TCP sockets in state alloc -node_sockstat_TCP_inuse: Number of TCP sockets in state inuse -node_sockstat_TCP_orphan: Number of TCP sockets in state orphan -node_sockstat_TCP_tw: Number of TCP sockets in state tw -node_netstat_Tcp_CurrEstab: Statistic TcpCurrEstab -node_sockstat_sockets_used: Number of IPv4 sockets in use - -# [kafka_exporter] -kafka_brokers: count of kafka_brokers (gauge) -kafka_topic_partitions: Number of partitions for this Topic (gauge) -kafka_topic_partition_current_offset: Current Offset of a Broker at Topic/Partition (gauge) -kafka_consumergroup_current_offset: Current Offset of a ConsumerGroup at Topic/Partition (gauge) -kafka_consumer_lag_millis: Current approximation of consumer lag for a ConsumerGroup at Topic/Partition (gauge) -kafka_topic_partition_under_replicated_partition: 1 if Topic/Partition is under Replicated - -# [zookeeper_exporter] -zk_znode_count: The total count of znodes stored -zk_ephemerals_count: The number of Ephemerals nodes -zk_watch_count: The number of watchers setup over Zookeeper nodes. -zk_approximate_data_size: Size of data in bytes that a zookeeper server has in its data tree -zk_outstanding_requests: Number of currently executing requests -zk_packets_sent: Count of the number of zookeeper packets sent from a server -zk_packets_received: Count of the number of zookeeper packets received by a server -zk_num_alive_connections: Number of active clients connected to a zookeeper server -zk_open_file_descriptor_count: Number of file descriptors that a zookeeper server has open -zk_max_file_descriptor_count: Maximum number of file descriptors that a zookeeper server can open -zk_avg_latency: Average time in milliseconds for requests to be processed -zk_min_latency: Maximum time in milliseconds for a request to be processed -zk_max_latency: Minimum time in milliseconds for a request to be processed \ No newline at end of file diff --git a/docker/n9eetc/script/notify.bak.py b/docker/n9eetc/script/notify.bak.py deleted file mode 100644 index bd240cbda167779abb1388a0bba4eaeb33bff2de..0000000000000000000000000000000000000000 --- a/docker/n9eetc/script/notify.bak.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -import sys -import json -import urllib2 -import smtplib -from email.mime.text import MIMEText - -reload(sys) -sys.setdefaultencoding('utf8') - -notify_channel_funcs = { - "email":"email", - "sms":"sms", - "voice":"voice", - "dingtalk":"dingtalk", - "wecom":"wecom", - "feishu":"feishu" -} - -mail_host = "smtp.163.com" -mail_port = 994 -mail_user = "ulricqin" -mail_pass = "password" -mail_from = "ulricqin@163.com" - -class Sender(object): - @classmethod - def send_email(cls, payload): - if mail_user == "ulricqin" and mail_pass == "password": - print("invalid smtp configuration") - return - - users = payload.get('event').get("notify_users_obj") - - emails = {} - for u in users: - if u.get("email"): - emails[u.get("email")] = 1 - - if not emails: - return - - recipients = emails.keys() - mail_body = payload.get('tpls').get("mailbody.tpl", "mailbody.tpl not found") - message = MIMEText(mail_body, 'html', 'utf-8') - message['From'] = mail_from - message['To'] = ", ".join(recipients) - message["Subject"] = payload.get('tpls').get("subject.tpl", "subject.tpl not found") - - try: - smtp = smtplib.SMTP_SSL(mail_host, mail_port) - smtp.login(mail_user, mail_pass) - smtp.sendmail(mail_from, recipients, message.as_string()) - smtp.close() - except smtplib.SMTPException, error: - print(error) - - @classmethod - def send_wecom(cls, payload): - users = payload.get('event').get("notify_users_obj") - - tokens = {} - - for u in users: - contacts = u.get("contacts") - if contacts.get("wecom_robot_token", ""): - tokens[contacts.get("wecom_robot_token", "")] = 1 - - opener = urllib2.build_opener(urllib2.HTTPHandler()) - method = "POST" - - for t in tokens: - url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={}".format(t) - body = { - "msgtype": "markdown", - "markdown": { - "content": payload.get('tpls').get("wecom.tpl", "wecom.tpl not found") - } - } - request = urllib2.Request(url, data=json.dumps(body)) - request.add_header("Content-Type",'application/json;charset=utf-8') - request.get_method = lambda: method - try: - connection = opener.open(request) - print(connection.read()) - except urllib2.HTTPError, error: - print(error) - - @classmethod - def send_dingtalk(cls, payload): - event = payload.get('event') - users = event.get("notify_users_obj") - - rule_name = event.get("rule_name") - event_state = "Triggered" - if event.get("is_recovered"): - event_state = "Recovered" - - tokens = {} - phones = {} - - for u in users: - if u.get("phone"): - phones[u.get("phone")] = 1 - - contacts = u.get("contacts") - if contacts.get("dingtalk_robot_token", ""): - tokens[contacts.get("dingtalk_robot_token", "")] = 1 - - opener = urllib2.build_opener(urllib2.HTTPHandler()) - method = "POST" - - for t in tokens: - url = "https://oapi.dingtalk.com/robot/send?access_token={}".format(t) - body = { - "msgtype": "markdown", - "markdown": { - "title": "{} - {}".format(event_state, rule_name), - "text": payload.get('tpls').get("dingtalk.tpl", "dingtalk.tpl not found") + ' '.join(["@"+i for i in phones.keys()]) - }, - "at": { - "atMobiles": phones.keys(), - "isAtAll": False - } - } - request = urllib2.Request(url, data=json.dumps(body)) - request.add_header("Content-Type",'application/json;charset=utf-8') - request.get_method = lambda: method - try: - connection = opener.open(request) - print(connection.read()) - except urllib2.HTTPError, error: - print(error) - - @classmethod - def send_feishu(cls, payload): - users = payload.get('event').get("notify_users_obj") - - tokens = {} - phones = {} - - for u in users: - if u.get("phone"): - phones[u.get("phone")] = 1 - - contacts = u.get("contacts") - if contacts.get("feishu_robot_token", ""): - tokens[contacts.get("feishu_robot_token", "")] = 1 - - opener = urllib2.build_opener(urllib2.HTTPHandler()) - method = "POST" - - for t in tokens: - url = "https://open.feishu.cn/open-apis/bot/v2/hook/{}".format(t) - body = { - "msg_type": "text", - "content": { - "text": payload.get('tpls').get("feishu.tpl", "feishu.tpl not found") - }, - "at": { - "atMobiles": phones.keys(), - "isAtAll": False - } - } - request = urllib2.Request(url, data=json.dumps(body)) - request.add_header("Content-Type",'application/json;charset=utf-8') - request.get_method = lambda: method - try: - connection = opener.open(request) - print(connection.read()) - except urllib2.HTTPError, error: - print(error) - - @classmethod - def send_sms(cls, payload): - users = payload.get('event').get("notify_users_obj") - phones = {} - for u in users: - if u.get("phone"): - phones[u.get("phone")] = 1 - if phones: - print("send_sms not implemented, phones: {}".format(phones.keys())) - - @classmethod - def send_voice(cls, payload): - users = payload.get('event').get("notify_users_obj") - phones = {} - for u in users: - if u.get("phone"): - phones[u.get("phone")] = 1 - if phones: - print("send_voice not implemented, phones: {}".format(phones.keys())) - -def main(): - payload = json.load(sys.stdin) - with open(".payload", 'w') as f: - f.write(json.dumps(payload, indent=4)) - for ch in payload.get('event').get('notify_channels'): - send_func_name = "send_{}".format(notify_channel_funcs.get(ch.strip())) - if not hasattr(Sender, send_func_name): - print("function: {} not found", send_func_name) - continue - send_func = getattr(Sender, send_func_name) - send_func(payload) - -def hello(): - print("hello nightingale") - -if __name__ == "__main__": - if len(sys.argv) == 1: - main() - elif sys.argv[1] == "hello": - hello() - else: - print("I am confused") \ No newline at end of file diff --git a/docker/n9eetc/script/notify.py b/docker/n9eetc/script/notify.py deleted file mode 100755 index f5c24eb90a0162af878256fb81467928cebb73f3..0000000000000000000000000000000000000000 --- a/docker/n9eetc/script/notify.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -import sys -import json - -class Sender(object): - @classmethod - def send_email(cls, payload): - # already done in go code - pass - - @classmethod - def send_wecom(cls, payload): - # already done in go code - pass - - @classmethod - def send_dingtalk(cls, payload): - # already done in go code - pass - - @classmethod - def send_feishu(cls, payload): - # already done in go code - pass - - @classmethod - def send_mm(cls, payload): - # already done in go code - pass - - @classmethod - def send_sms(cls, payload): - users = payload.get('event').get("notify_users_obj") - phones = {} - for u in users: - if u.get("phone"): - phones[u.get("phone")] = 1 - if phones: - print("send_sms not implemented, phones: {}".format(phones.keys())) - - @classmethod - def send_voice(cls, payload): - users = payload.get('event').get("notify_users_obj") - phones = {} - for u in users: - if u.get("phone"): - phones[u.get("phone")] = 1 - if phones: - print("send_voice not implemented, phones: {}".format(phones.keys())) - -def main(): - payload = json.load(sys.stdin) - with open(".payload", 'w') as f: - f.write(json.dumps(payload, indent=4)) - for ch in payload.get('event').get('notify_channels'): - send_func_name = "send_{}".format(ch.strip()) - if not hasattr(Sender, send_func_name): - print("function: {} not found", send_func_name) - continue - send_func = getattr(Sender, send_func_name) - send_func(payload) - -def hello(): - print("hello nightingale") - -if __name__ == "__main__": - if len(sys.argv) == 1: - main() - elif sys.argv[1] == "hello": - hello() - else: - print("I am confused") \ No newline at end of file diff --git a/docker/n9eetc/script/notify/Makefile b/docker/n9eetc/script/notify/Makefile deleted file mode 100644 index 4d4f7d04ac92324d94ba1dda5ef40b5cff7468d9..0000000000000000000000000000000000000000 --- a/docker/n9eetc/script/notify/Makefile +++ /dev/null @@ -1,9 +0,0 @@ - -.phony: all -all: plugin - -.phony: plugin -plugin: - export GOPROXY=http://goproxy.cn,direct - go build -buildmode=plugin -o notify.so notify.go - diff --git a/docker/n9eetc/script/notify/README.md b/docker/n9eetc/script/notify/README.md deleted file mode 100644 index 73d977f9fb95349dc950e5d85a94f092b43bb9e5..0000000000000000000000000000000000000000 --- a/docker/n9eetc/script/notify/README.md +++ /dev/null @@ -1,35 +0,0 @@ -通过go plugin模式处理告警通知 ---- - -相比于调用py脚本方式,该方式一般无需考虑依赖问题 - -### (1) 编写动态链接库逻辑 - -```go -package main - -type inter interface { - Descript() string - Notify([]byte) -} - -// 0、Descript 可用于该插件在 server 中的描述 -// 1、在 Notify 方法中实现要处理的自定义逻辑 -``` - -实现以上接口的 `struct` 实例即为合法 `plugin` - -### (2) 构建链接库 - -参考 `notify.go` 实现方式,执行 `make` 后可以看到生成一个 `notify.so` 链接文件,放到 n9e 对应项目位置即可 - -### (3) 更新 n9e 配置 - -```text -[Alerting.CallPlugin] -Enable = false -PluginPath = "./etc/script/notify.so" -# 注意此处caller必须在notify.so中作为变量暴露,首字母必须大写才能暴露 -Caller = "N9eCaller" -``` - diff --git a/docker/n9eetc/script/notify/notify.go b/docker/n9eetc/script/notify/notify.go deleted file mode 100644 index b8614c453184bdf5a5c7c4de4780a75b313e23f4..0000000000000000000000000000000000000000 --- a/docker/n9eetc/script/notify/notify.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "github.com/tidwall/gjson" -) - -// the caller can be called for alerting notify by complete this interface -type inter interface { - Descript() string - Notify([]byte) -} - -// N9E complete -type N9EPlugin struct { - Name string - Description string - BuildAt string -} - -func (n *N9EPlugin) Descript() string { - return fmt.Sprintf("%s: %s", n.Name, n.Description) -} - -func (n *N9EPlugin) Notify(bs []byte) { - var channels = []string{ - "dingtalk_robot_token", - "wecom_robot_token", - "feishu_robot_token", - "telegram_robot_token", - } - for _, ch := range channels { - if ret := gjson.GetBytes(bs, ch); ret.Exists() { - fmt.Printf("do something...") - } - } -} - -// will be loaded for alertingCall , The first letter must be capitalized to be exported -var N9eCaller = N9EPlugin{ - Name: "n9e", - Description: "演示告警通过动态链接库方式通知", - BuildAt: time.Now().Local().Format("2006/01/02 15:04:05"), -} diff --git a/docker/n9eetc/server.conf b/docker/n9eetc/server.conf deleted file mode 100644 index 18d6f97b8fd0dda82eb4bba22184e6bd213de9c2..0000000000000000000000000000000000000000 --- a/docker/n9eetc/server.conf +++ /dev/null @@ -1,211 +0,0 @@ -# debug, release -RunMode = "release" - -# my cluster name -ClusterName = "Default" - -# Default busigroup Key name -# do not change -BusiGroupLabelKey = "busigroup" - -# sleep x seconds, then start judge engine -EngineDelay = 60 - -DisableUsageReport = true - -# config | database -ReaderFrom = "config" - -[Log] -# log write dir -Dir = "logs" -# log level: DEBUG INFO WARNING ERROR -Level = "INFO" -# stdout, stderr, file -Output = "stdout" -# # rotate by time -# KeepHours: 4 -# # rotate by size -# RotateNum = 3 -# # unit: MB -# RotateSize = 256 - -[HTTP] -# http listening address -Host = "0.0.0.0" -# http listening port -Port = 19000 -# https cert file path -CertFile = "" -# https key file path -KeyFile = "" -# whether print access log -PrintAccessLog = false -# whether enable pprof -PProf = false -# http graceful shutdown timeout, unit: s -ShutdownTimeout = 30 -# max content length: 64M -MaxContentLength = 67108864 -# http server read timeout, unit: s -ReadTimeout = 20 -# http server write timeout, unit: s -WriteTimeout = 40 -# http server idle timeout, unit: s -IdleTimeout = 120 - -# [BasicAuth] -# user002 = "ccc26da7b9aba533cbb263a36c07dcc9" - -[Heartbeat] -# auto detect if blank -IP = "" -# unit ms -Interval = 1000 - -[SMTP] -Host = "smtp.163.com" -Port = 994 -User = "username" -Pass = "password" -From = "username@163.com" -InsecureSkipVerify = true -Batch = 5 - -[Alerting] -# timeout settings, unit: ms, default: 30000ms -Timeout=30000 -TemplatesDir = "./etc/template" -NotifyConcurrency = 10 -# use builtin go code notify -NotifyBuiltinChannels = ["email", "dingtalk", "wecom", "feishu", "mm", "telegram"] - -[Alerting.CallScript] -# built in sending capability in go code -# so, no need enable script sender -Enable = false -ScriptPath = "./etc/script/notify.py" - -[Alerting.CallPlugin] -Enable = false -# use a plugin via `go build -buildmode=plugin -o notify.so` -PluginPath = "./etc/script/notify.so" -# The first letter must be capitalized to be exported -Caller = "N9eCaller" - -[Alerting.RedisPub] -Enable = false -# complete redis key: ${ChannelPrefix} + ${Cluster} -ChannelPrefix = "/alerts/" - -[Alerting.Webhook] -Enable = false -Url = "http://a.com/n9e/callback" -BasicAuthUser = "" -BasicAuthPass = "" -Timeout = "5s" -Headers = ["Content-Type", "application/json", "X-From", "N9E"] - -[NoData] -Metric = "target_up" -# unit: second -Interval = 120 - -[Ibex] -# callback: ${ibex}/${tplid}/${host} -Address = "ibex:10090" -# basic auth -BasicAuthUser = "ibex" -BasicAuthPass = "ibex" -# unit: ms -Timeout = 3000 - -[Redis] -# address, ip:port -Address = "redis:6379" -# requirepass -Password = "" -# # db -# DB = 0 - -[DB] -# postgres: host=%s port=%s user=%s dbname=%s password=%s sslmode=%s -DSN="root:1234@tcp(mysql:3306)/n9e_v5?charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true" -# enable debug mode or not -Debug = false -# mysql postgres -DBType = "mysql" -# unit: s -MaxLifetime = 7200 -# max open connections -MaxOpenConns = 150 -# max idle connections -MaxIdleConns = 50 -# table prefix -TablePrefix = "" -# enable auto migrate or not -# EnableAutoMigrate = false - -[Reader] -# prometheus base url -Url = "http://prometheus:9090" -# Basic auth username -BasicAuthUser = "" -# Basic auth password -BasicAuthPass = "" -# timeout settings, unit: ms -Timeout = 30000 -DialTimeout = 3000 -MaxIdleConnsPerHost = 100 - -[WriterOpt] -# queue channel count -QueueCount = 1000 -# queue max size -QueueMaxSize = 1000000 -# once pop samples number from queue -QueuePopSize = 1000 -# metric or ident -ShardingKey = "ident" - -[[Writers]] -Url = "http://prometheus:9090/api/v1/write" -# Basic auth username -BasicAuthUser = "" -# Basic auth password -BasicAuthPass = "" -# timeout settings, unit: ms -Timeout = 10000 -DialTimeout = 3000 -TLSHandshakeTimeout = 30000 -ExpectContinueTimeout = 1000 -IdleConnTimeout = 90000 -# time duration, unit: ms -KeepAlive = 30000 -MaxConnsPerHost = 0 -MaxIdleConns = 100 -MaxIdleConnsPerHost = 100 -# [[Writers.WriteRelabels]] -# Action = "replace" -# SourceLabels = ["__address__"] -# Regex = "([^:]+)(?::\\d+)?" -# Replacement = "$1:80" -# TargetLabel = "__address__" - -# [[Writers]] -# Url = "http://m3db:7201/api/v1/prom/remote/write" -# # Basic auth username -# BasicAuthUser = "" -# # Basic auth password -# BasicAuthPass = "" -# # timeout settings, unit: ms -# Timeout = 30000 -# DialTimeout = 10000 -# TLSHandshakeTimeout = 30000 -# ExpectContinueTimeout = 1000 -# IdleConnTimeout = 90000 -# # time duration, unit: ms -# KeepAlive = 30000 -# MaxConnsPerHost = 0 -# MaxIdleConns = 100 -# MaxIdleConnsPerHost = 100 diff --git a/docker/n9eetc/template/dingtalk.tpl b/docker/n9eetc/template/dingtalk.tpl deleted file mode 100644 index 4525751244aeb2d9603a3c30e2af6a6a35f4d37f..0000000000000000000000000000000000000000 --- a/docker/n9eetc/template/dingtalk.tpl +++ /dev/null @@ -1,11 +0,0 @@ -#### {{if .IsRecovered}}S{{.Severity}} - Recovered - {{.RuleName}}{{else}}S{{.Severity}} - Triggered - {{.RuleName}}{{end}} - ---- - -- **规则标题**: {{.RuleName}}{{if .RuleNote}} -- **规则备注**: {{.RuleNote}}{{end}} -- **监控指标**: {{.TagsJSON}} -- {{if .IsRecovered}}**恢复时间**:{{timeformat .LastEvalTime}}{{else}}**触发时间**: {{timeformat .TriggerTime}} -- **触发时值**: {{.TriggerValue}}{{end}} -- **发送时间**: {{timestamp}} - diff --git a/docker/n9eetc/template/feishu.tpl b/docker/n9eetc/template/feishu.tpl deleted file mode 100644 index 52e261938bcfc9789710ac51dc7bf74b1f620bda..0000000000000000000000000000000000000000 --- a/docker/n9eetc/template/feishu.tpl +++ /dev/null @@ -1,7 +0,0 @@ -级别状态: S{{.Severity}} {{if .IsRecovered}}Recovered{{else}}Triggered{{end}} -规则名称: {{.RuleName}}{{if .RuleNote}} -规则备注: {{.RuleNote}}{{end}} -监控指标: {{.TagsJSON}} -{{if .IsRecovered}}恢复时间:{{timeformat .LastEvalTime}}{{else}}触发时间: {{timeformat .TriggerTime}} -触发时值: {{.TriggerValue}}{{end}} -发送时间: {{timestamp}} \ No newline at end of file diff --git a/docker/n9eetc/template/mailbody.tpl b/docker/n9eetc/template/mailbody.tpl deleted file mode 100644 index 3fc4cb568c0b9d92bc2d8ae70051240aaf4533f1..0000000000000000000000000000000000000000 --- a/docker/n9eetc/template/mailbody.tpl +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - 夜莺告警通知 - - - -
-
-
-

{{.RuleName}}

-

-
- -
- -
- - - {{if .IsRecovered}} - - - - - {{else}} - - - - - {{end}} - - - - - - - - - - - - - - - {{if .IsRecovered}} - - - - - {{else}} - - - - - - - - - {{end}} - - - - - - - - - - - -
级别状态:S{{.Severity}} Recovered
级别状态:S{{.Severity}} Triggered
策略备注:{{.RuleNote}}
设备备注:{{.TargetNote}}
监控指标:{{.TagsJSON}}
恢复时间:{{timeformat .LastEvalTime}}
触发时值:{{.TriggerValue}}
触发时间: - {{timeformat .TriggerTime}} -
发送时间: - {{timestamp}} -
PromQL: - {{.PromQl}} -
- -
- -
- -
-
-
-
- - \ No newline at end of file diff --git a/docker/n9eetc/template/telegram.tpl b/docker/n9eetc/template/telegram.tpl deleted file mode 100644 index 5a16cd61749fbc2b1d32eab35822ca1bf67d45e7..0000000000000000000000000000000000000000 --- a/docker/n9eetc/template/telegram.tpl +++ /dev/null @@ -1,7 +0,0 @@ -**级别状态**: {{if .IsRecovered}}S{{.Severity}} Recovered{{else}}S{{.Severity}} Triggered{{end}} -**规则标题**: {{.RuleName}}{{if .RuleNote}} -**规则备注**: {{.RuleNote}}{{end}} -**监控指标**: {{.TagsJSON}} -{{if .IsRecovered}}**恢复时间**:{{timeformat .LastEvalTime}}{{else}}**触发时间**: {{timeformat .TriggerTime}} -**触发时值**: {{.TriggerValue}}{{end}} -**发送时间**: {{timestamp}} \ No newline at end of file diff --git a/docker/n9eetc/template/wecom.tpl b/docker/n9eetc/template/wecom.tpl deleted file mode 100644 index 5a16cd61749fbc2b1d32eab35822ca1bf67d45e7..0000000000000000000000000000000000000000 --- a/docker/n9eetc/template/wecom.tpl +++ /dev/null @@ -1,7 +0,0 @@ -**级别状态**: {{if .IsRecovered}}S{{.Severity}} Recovered{{else}}S{{.Severity}} Triggered{{end}} -**规则标题**: {{.RuleName}}{{if .RuleNote}} -**规则备注**: {{.RuleNote}}{{end}} -**监控指标**: {{.TagsJSON}} -{{if .IsRecovered}}**恢复时间**:{{timeformat .LastEvalTime}}{{else}}**触发时间**: {{timeformat .TriggerTime}} -**触发时值**: {{.TriggerValue}}{{end}} -**发送时间**: {{timestamp}} \ No newline at end of file diff --git a/docker/n9eetc/webapi.conf b/docker/n9eetc/webapi.conf deleted file mode 100644 index 9de88bbe0410fd4ec9d2e50770d8a792f5d7ac1f..0000000000000000000000000000000000000000 --- a/docker/n9eetc/webapi.conf +++ /dev/null @@ -1,226 +0,0 @@ -# debug, release -RunMode = "release" - -# # custom i18n dict config -# I18N = "./etc/i18n.json" - -# # custom i18n request header key -# I18NHeaderKey = "X-Language" - -# metrics descriptions -MetricsYamlFile = "./etc/metrics.yaml" - -BuiltinAlertsDir = "./etc/alerts" -BuiltinDashboardsDir = "./etc/dashboards" - -# config | api -ClustersFrom = "config" - -# using when ClustersFrom = "api" -ClustersFromAPIs = [] - -[[NotifyChannels]] -Label = "邮箱" -# do not change Key -Key = "email" - -[[NotifyChannels]] -Label = "钉钉机器人" -# do not change Key -Key = "dingtalk" - -[[NotifyChannels]] -Label = "企微机器人" -# do not change Key -Key = "wecom" - -[[NotifyChannels]] -Label = "飞书机器人" -# do not change Key -Key = "feishu" - -[[NotifyChannels]] -Label = "mm bot" -# do not change Key -Key = "mm" - -[[NotifyChannels]] -Label = "telegram机器人" -# do not change Key -Key = "telegram" - -[[ContactKeys]] -Label = "Wecom Robot Token" -# do not change Key -Key = "wecom_robot_token" - -[[ContactKeys]] -Label = "Dingtalk Robot Token" -# do not change Key -Key = "dingtalk_robot_token" - -[[ContactKeys]] -Label = "Feishu Robot Token" -# do not change Key -Key = "feishu_robot_token" - -[[ContactKeys]] -Label = "MatterMost Webhook URL" -# do not change Key -Key = "mm_webhook_url" - -[[ContactKeys]] -Label = "Telegram Robot Token" -# do not change Key -Key = "telegram_robot_token" - -[Log] -# log write dir -Dir = "logs" -# log level: DEBUG INFO WARNING ERROR -Level = "DEBUG" -# stdout, stderr, file -Output = "stdout" -# # rotate by time -# KeepHours: 4 -# # rotate by size -# RotateNum = 3 -# # unit: MB -# RotateSize = 256 - -[HTTP] -# http listening address -Host = "0.0.0.0" -# http listening port -Port = 18000 -# https cert file path -CertFile = "" -# https key file path -KeyFile = "" -# whether print access log -PrintAccessLog = true -# whether enable pprof -PProf = false -# http graceful shutdown timeout, unit: s -ShutdownTimeout = 30 -# max content length: 64M -MaxContentLength = 67108864 -# http server read timeout, unit: s -ReadTimeout = 20 -# http server write timeout, unit: s -WriteTimeout = 40 -# http server idle timeout, unit: s -IdleTimeout = 120 - -[JWTAuth] -# signing key -SigningKey = "5b94a0fd640fe2765af826acfe42d151" -# unit: min -AccessExpired = 1500 -# unit: min -RefreshExpired = 10080 -RedisKeyPrefix = "/jwt/" - -[ProxyAuth] -# if proxy auth enabled, jwt auth is disabled -Enable = false -# username key in http proxy header -HeaderUserNameKey = "X-User-Name" -DefaultRoles = ["Standard"] - -[BasicAuth] -user001 = "ccc26da7b9aba533cbb263a36c07dcc5" - -[AnonymousAccess] -PromQuerier = false -AlertDetail = false - -[LDAP] -Enable = false -Host = "ldap.example.org" -Port = 389 -BaseDn = "dc=example,dc=org" -# AD: manange@example.org -BindUser = "cn=manager,dc=example,dc=org" -BindPass = "*******" -# openldap format e.g. (&(uid=%s)) -# AD format e.g. (&(sAMAccountName=%s)) -AuthFilter = "(&(uid=%s))" -CoverAttributes = true -TLS = false -StartTLS = true -# ldap user default roles -DefaultRoles = ["Standard"] - -[LDAP.Attributes] -Nickname = "cn" -Phone = "mobile" -Email = "mail" - -[OIDC] -Enable = false -RedirectURL = "http://n9e.com/callback" -SsoAddr = "http://sso.example.org" -ClientId = "" -ClientSecret = "" -CoverAttributes = true -DefaultRoles = ["Standard"] - -[OIDC.Attributes] -Nickname = "nickname" -Phone = "phone_number" -Email = "email" - -[Redis] -# address, ip:port -Address = "redis:6379" -# requirepass -Password = "" -# # db -# DB = 0 - -[DB] -# postgres: host=%s port=%s user=%s dbname=%s password=%s sslmode=%s -DSN="root:1234@tcp(mysql:3306)/n9e_v5?charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true" -# enable debug mode or not -Debug = true -# mysql postgres -DBType = "mysql" -# unit: s -MaxLifetime = 7200 -# max open connections -MaxOpenConns = 150 -# max idle connections -MaxIdleConns = 50 -# table prefix -TablePrefix = "" -# enable auto migrate or not -# EnableAutoMigrate = false - -[[Clusters]] -# Prometheus cluster name -Name = "Default" -# Prometheus APIs base url -Prom = "http://prometheus:9090" -# Basic auth username -BasicAuthUser = "" -# Basic auth password -BasicAuthPass = "" -# timeout settings, unit: ms -Timeout = 30000 -DialTimeout = 3000 -MaxIdleConnsPerHost = 100 - -[Ibex] -Address = "http://ibex:10090" -# basic auth -BasicAuthUser = "ibex" -BasicAuthPass = "ibex" -# unit: ms -Timeout = 3000 - -[TargetMetrics] -TargetUp = '''max(max_over_time(target_up{ident=~"(%s)"}[%dm])) by (ident)''' -LoadPerCore = '''max(max_over_time(system_load_norm_1{ident=~"(%s)"}[%dm])) by (ident)''' -MemUtil = '''100-max(max_over_time(mem_available_percent{ident=~"(%s)"}[%dm])) by (ident)''' -DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)''' diff --git a/docker/prometc/prometheus.yml b/docker/prometc/prometheus.yml deleted file mode 100644 index cad869dccff7ea372bef2e3a0ad5d6718f395c81..0000000000000000000000000000000000000000 --- a/docker/prometc/prometheus.yml +++ /dev/null @@ -1,34 +0,0 @@ -# my global config -global: - scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. - evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. - # scrape_timeout is set to the global default (10s). - -# Alertmanager configuration -alerting: - alertmanagers: - - static_configs: - - targets: - # - alertmanager:9093 - -# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. -rule_files: - # - "first_rules.yml" - # - "second_rules.yml" - -# A scrape configuration containing exactly one endpoint to scrape: -# Here it's Prometheus itself. -scrape_configs: - # The job name is added as a label `job=` to any timeseries scraped from this config. - - job_name: 'prometheus' - - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - static_configs: - - targets: ['localhost:9090'] - - - job_name: 'n9e' - file_sd_configs: - - files: - - targets.json diff --git a/docker/prometc/targets.json b/docker/prometc/targets.json deleted file mode 100644 index 2f050d8f95861436bf1ac6e2469947477e685534..0000000000000000000000000000000000000000 --- a/docker/prometc/targets.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "targets": [ - "nwebapi:18000","nserver:19000" - ] - } -] diff --git a/etc/alerts/elasticsearch_by_exporter.json b/etc/alerts/elasticsearch_by_exporter.json deleted file mode 100644 index 7ed5cb2111395d8fb23132f2e3f5802435e04f36..0000000000000000000000000000000000000000 --- a/etc/alerts/elasticsearch_by_exporter.json +++ /dev/null @@ -1,392 +0,0 @@ -[ - { - "name": "Elastic Cluster Red status", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": " elasticsearch_cluster_health_status{color=\"red\"} == 1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchClusterRed" - ] - }, - { - "name": "Elastic Cluster Yellow status", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "elasticsearch_cluster_health_status{color=\"yellow\"} == 1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchClusterYellow" - ] - }, - { - "name": "Elasticsearch disk out of space of the instance", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "elasticsearch_filesystem_data_available_bytes / elasticsearch_filesystem_data_size_bytes * 100 < 10", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchDiskOutOfSpace" - ] - }, - { - "name": "Elasticsearch disk space low of the instance", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "elasticsearch_filesystem_data_available_bytes / elasticsearch_filesystem_data_size_bytes * 100 < 20", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchDiskSpaceLow" - ] - }, - { - "name": "Elasticsearch Heap Usage Too High of the instance", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "(elasticsearch_jvm_memory_used_bytes{area=\"heap\"} / elasticsearch_jvm_memory_max_bytes{area=\"heap\"}) * 100 > 90", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchHeapUsageTooHigh" - ] - }, - { - "name": "Elasticsearch Heap Usage warning of the instance", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "(elasticsearch_jvm_memory_used_bytes{area=\"heap\"} / elasticsearch_jvm_memory_max_bytes{area=\"heap\"}) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchHeapUsageWarning" - ] - }, - { - "name": "Elasticsearch initializing shards of the instance", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 900, - "prom_ql": "elasticsearch_cluster_health_initializing_shards > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchInitializingShards" - ] - }, - { - "name": "Elasticsearch no new documents of the instance", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 300, - "prom_ql": "rate(elasticsearch_indices_docs{es_data_node=\"true\"}[5m]) == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchNoNewDocuments" - ] - }, - { - "name": "Elasticsearch pending tasks of the instance", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 900, - "prom_ql": "elasticsearch_cluster_health_number_of_pending_tasks > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchPendingTasks" - ] - }, - { - "name": "Elasticsearch relocation shards of the instance", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 900, - "prom_ql": "elasticsearch_cluster_health_relocating_shards > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchRelocationShards" - ] - }, - { - "name": "Elasticsearch unassigned shards of the instance", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "elasticsearch_cluster_health_unassigned_shards > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchUnassignedShards" - ] - }, - { - "name": "Elasticsearch Unhealthy Data Nodes", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "elasticsearch_cluster_health_number_of_data_nodes < number_of_data_nodes", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchHealthyDataNodes" - ] - }, - { - "name": "Elasticsearch Unhealthy Nodes", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": " elasticsearch_cluster_health_number_of_nodes < number_of_nodes", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ElasticsearchHealthyNodes" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/http_response_by_categraf.json b/etc/alerts/http_response_by_categraf.json deleted file mode 100644 index 3908400f96626e722bea94bc0d39a5712b9cc465..0000000000000000000000000000000000000000 --- a/etc/alerts/http_response_by_categraf.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "name": "HTTP地址探测失败", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "http_response_result_code != 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] \ No newline at end of file diff --git a/etc/alerts/linux_by_categraf.json b/etc/alerts/linux_by_categraf.json deleted file mode 100644 index ee9d2be9e86efbeb5bf45cbbf8d38ec243c0a224..0000000000000000000000000000000000000000 --- a/etc/alerts/linux_by_categraf.json +++ /dev/null @@ -1,243 +0,0 @@ -[ - { - "name": "监控对象失联", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "max_over_time(target_up[130s]) == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "机器负载-CPU较高,请关注", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "cpu_usage_idle{cpu=\"cpu-total\"} < 25", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "机器负载-内存较高,请关注", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "mem_available_percent < 25", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "硬盘-IO有点繁忙", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "rate(diskio_io_time[1m])/10 > 99", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "硬盘-预计再有4小时写满", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "predict_linear(disk_free[1h], 4*3600) < 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网卡-入向有丢包", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "increase(net_drop_in[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网卡-出向有丢包", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "increase(net_drop_out[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网络连接-TME_WAIT数量超过2万", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "netstat_tcp_time_wait > 20000", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } -] - \ No newline at end of file diff --git a/etc/alerts/linux_by_exporter.json b/etc/alerts/linux_by_exporter.json deleted file mode 100644 index 2a68c951809610da9c73b3603769e800ece60727..0000000000000000000000000000000000000000 --- a/etc/alerts/linux_by_exporter.json +++ /dev/null @@ -1,344 +0,0 @@ -[ - { - "name": "inode资源不足-使用率超过90", - "note": "", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "(100 - ((node_filesystem_files_free * 100) / node_filesystem_files))>90", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "内存资源不足-利用率大于75%", - "note": "需要扩容或者升级配置了", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - (node_memory_Cached_bytes + node_memory_Buffers_bytes))/node_memory_MemTotal_bytes*100 > 75", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [ - "dingtalk" - ], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "内存资源不足-利用率大于95%", - "note": "需要扩容或者升级配置了", - "severity": 1, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - (node_memory_Cached_bytes + node_memory_Buffers_bytes))/node_memory_MemTotal_bytes*100 > 95", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [ - "dingtalk" - ], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "文件句柄不足-使用率超过90%", - "note": "可以将文件句柄limit调大,或者扩容", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "(node_filefd_allocated{instance=\"$node\"}/node_filefd_maximum{instance=\"$node\"}*100) > 90", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "某磁盘无法正常读写", - "note": "", - "severity": 1, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "(node_filesystem_device_error{instance=\"$node\",mountpoint!~\"/var/lib/.*\",mountpoint!~\"/run.*\"}) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "磁盘需要清理了-利用率达到92%", - "note": "", - "severity": 1, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "(100 - ((node_filesystem_avail_bytes * 100) / node_filesystem_size_bytes) ) > 92 ", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [ - "dingtalk" - ], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "系统conntrack需要调整-使用率超过80%", - "note": "", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "node_nf_conntrack_entries / node_nf_conntrack_entries_limit*100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "系统出现oom", - "note": "", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "increase(node_vmstat_oom_kill[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网卡入方向丢包", - "note": "", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "rate(node_network_receive_drop_total{device=~\"e.*\"}[1m]) > 3", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网卡出方向丢包", - "note": "", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "rate(node_network_transmit_drop_total{device=~\"e.*\"}[1m]) > 3", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "计算资源不足-机器每个核平均负载大于10", - "note": "需要扩容或者升级配置了", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "avg (node_load1) by (instance)/count(count(node_cpu_seconds_total) by (cpu,instance)) by (instance) >10", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "运行进程数过多-超过3000", - "note": "建议扩容", - "severity": 2, - "disabled": 1, - "prom_for_duration": 60, - "prom_ql": "node_procs_running > 3000", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } -] \ No newline at end of file diff --git a/etc/alerts/linux_by_telegraf.json b/etc/alerts/linux_by_telegraf.json deleted file mode 100644 index d64c6ddd39ce64b391814a892484967665739f9c..0000000000000000000000000000000000000000 --- a/etc/alerts/linux_by_telegraf.json +++ /dev/null @@ -1,393 +0,0 @@ -[ - { - "name": "有地址PING不通,请注意", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "ping_result_code != 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "有监控对象失联", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "target_up != 1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "有端口探测失败,请注意", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "net_response_result_code != 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "机器负载-CPU较高,请关注", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "cpu_usage_idle{cpu=\"cpu-total\"} < 25", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "机器负载-内存较高,请关注", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "mem_available_percent < 25", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "硬盘-IO非常繁忙", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "rate(diskio_io_time[1m])/10 > 99", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "硬盘-预计再有4小时写满", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "predict_linear(disk_free[1h], 4*3600) < 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网卡-入向有丢包", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "increase(net_drop_in[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网卡-出向有丢包", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "increase(net_drop_out[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "网络连接-TME_WAIT数量超过2万", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "netstat_tcp_time_wait > 20000", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "进程监控-有进程数为0,某进程可能挂了", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "procstat_lookup_running == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "进程监控-进程句柄限制过小", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "procstat_rlimit_num_fds_soft < 2048", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "进程监控-采集失败", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "procstat_lookup_result_code != 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] - \ No newline at end of file diff --git a/etc/alerts/mongo_by_exporter.json b/etc/alerts/mongo_by_exporter.json deleted file mode 100644 index ba4be4cb05c66b86c6cddab97c9da05bf42aa3aa..0000000000000000000000000000000000000000 --- a/etc/alerts/mongo_by_exporter.json +++ /dev/null @@ -1,242 +0,0 @@ -[ - { - "name": "Mongo出现Assert错误", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 1800, - "prom_ql": "rate(mongodb_asserts_total{type=~\"regular|message\"}[5m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoAssertsDetected" - ] - }, - { - "name": "Mongo出现游标超时", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 1800, - "prom_ql": "rate(mongodb_mongod_metrics_cursor_timed_out_total[5m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoRecurrentCursorTimeout" - ] - }, - { - "name": "Mongo出现页错误中断", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 1800, - "prom_ql": "rate(mongodb_extra_info_page_faults_total[5m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoRecurrentMemoryPageFaults" - ] - }, - { - "name": "Mongo刚刚有重启,请注意", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mongodb_instance_uptime_seconds < 60", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoRestarted" - ] - }, - { - "name": "Mongo副本集主从延迟超过30s", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "mongodb_mongod_replset_member_replication_lag > 30", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoSlaveReplicationLag(>30s)" - ] - }, - { - "name": "Mongo实例挂了", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "MongoServerDown", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoServerDown" - ] - }, - { - "name": "Mongo操作平均耗时超过250秒", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 600, - "prom_ql": "rate(mongodb_mongod_op_latencies_latency_total[5m]) / rate(mongodb_mongod_op_latencies_ops_total[5m]) > 250000", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoOperationHighLatency" - ] - }, - { - "name": "Mongo连接数已超过80%", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mongodb_connections{state=\"current\"}) / avg by (instance) (mongodb_connections{state=\"available\"}) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MongoTooManyConnections(>80%)" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/mysql_by_categraf.json b/etc/alerts/mysql_by_categraf.json deleted file mode 100644 index d5a6a94c62f772d7d994b529d0d0336b798a66d6..0000000000000000000000000000000000000000 --- a/etc/alerts/mysql_by_categraf.json +++ /dev/null @@ -1,302 +0,0 @@ -[ - { - "name": "MysqlInnodbLogWaits", - "note": "MySQL innodb log writes stalling", - "severity": 2, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "rate(mysql_global_status_innodb_log_waits[15m]) > 10", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlInnodbLogWaits" - ] - }, - { - "name": "MysqlSlaveIoThreadNotRunning", - "note": "MySQL Slave IO thread not running", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_slave_status_master_server_id > 0 and ON (instance) mysql_slave_status_slave_io_running == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlaveIoThreadNotRunning" - ] - }, - { - "name": "MysqlSlaveReplicationLag", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "mysql_slave_status_master_server_id > 0 and ON (instance) (mysql_slave_status_seconds_behind_master - mysql_slave_status_sql_delay) > 30", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlaveReplicationLag" - ] - }, - { - "name": "MysqlSlaveSqlThreadNotRunning", - "note": "MySQL Slave SQL thread not running", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_slave_status_master_server_id > 0 and ON (instance) mysql_slave_status_slave_sql_running == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlaveSqlThreadNotRunning" - ] - }, - { - "name": "Mysql刚刚有重启,请注意", - "note": "MySQL has just been restarted, less than one minute ago", - "severity": 3, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_global_status_uptime < 60", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlRestarted" - ] - }, - { - "name": "Mysql实例挂了", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_up == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlDown" - ] - }, - { - "name": "Mysql打开了很多文件句柄,请注意", - "note": "More than 80% of MySQL files open", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mysql_global_status_open_files) / avg by (instance)(mysql_global_variables_open_files_limit) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlHighOpenFiles" - ] - }, - { - "name": "Mysql最近一分钟有慢查询出现", - "note": "MySQL server mysql has some new slow query", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "increase(mysql_global_status_slow_queries[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlowQueries" - ] - }, - { - "name": "Mysql有超过60%的连接是running状态", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mysql_global_status_threads_running) / avg by (instance) (mysql_global_variables_max_connections) * 100 > 60", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlHighThreadsRunning" - ] - }, - { - "name": "Mysql连接数已超过80%", - "note": "More than 80% of MySQL connections are in use", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mysql_global_status_threads_connected) / avg by (instance) (mysql_global_variables_max_connections) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlTooManyConnections" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/mysql_by_exporter.json b/etc/alerts/mysql_by_exporter.json deleted file mode 100644 index d894cda9a35dbaa0f9445a7ee744dab308ca7386..0000000000000000000000000000000000000000 --- a/etc/alerts/mysql_by_exporter.json +++ /dev/null @@ -1,302 +0,0 @@ -[ - { - "name": "MysqlInnodbLogWaits", - "note": "MySQL innodb log writes stalling", - "severity": 2, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "rate(mysql_global_status_innodb_log_waits[15m]) > 10", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlInnodbLogWaits" - ] - }, - { - "name": "MysqlSlaveIoThreadNotRunning", - "note": "MySQL Slave IO thread not running", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_slave_status_master_server_id > 0 and ON (instance) mysql_slave_status_slave_io_running == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlaveIoThreadNotRunning" - ] - }, - { - "name": "MysqlSlaveReplicationLag", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "mysql_slave_status_master_server_id > 0 and ON (instance) (mysql_slave_status_seconds_behind_master - mysql_slave_status_sql_delay) > 30", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlaveReplicationLag" - ] - }, - { - "name": "MysqlSlaveSqlThreadNotRunning", - "note": "MySQL Slave SQL thread not running", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_slave_status_master_server_id > 0 and ON (instance) mysql_slave_status_slave_sql_running == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlaveSqlThreadNotRunning" - ] - }, - { - "name": "Mysql刚刚有重启,请注意", - "note": "MySQL has just been restarted, less than one minute ago", - "severity": 3, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_global_status_uptime < 60", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlRestarted" - ] - }, - { - "name": "Mysql实例挂了", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "mysql_up == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlDown" - ] - }, - { - "name": "Mysql打开了很多文件句柄,请注意", - "note": "More than 80% of MySQL files open", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mysql_global_status_innodb_num_open_files) / avg by (instance)(mysql_global_variables_open_files_limit) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlHighOpenFiles" - ] - }, - { - "name": "Mysql最近一分钟有慢查询出现", - "note": "MySQL server mysql has some new slow query", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "increase(mysql_global_status_slow_queries[1m]) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlSlowQueries" - ] - }, - { - "name": "Mysql有超过60%的连接是running状态", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mysql_global_status_threads_running) / avg by (instance) (mysql_global_variables_max_connections) * 100 > 60", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlHighThreadsRunning" - ] - }, - { - "name": "Mysql连接数已超过80%", - "note": "More than 80% of MySQL connections are in use", - "severity": 2, - "disabled": 0, - "prom_for_duration": 120, - "prom_ql": "avg by (instance) (mysql_global_status_threads_connected) / avg by (instance) (mysql_global_variables_max_connections) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=MysqlTooManyConnections" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/net_response_by_categraf.json b/etc/alerts/net_response_by_categraf.json deleted file mode 100644 index a55961e4f4241e471a03e61343f1e50b3576d916..0000000000000000000000000000000000000000 --- a/etc/alerts/net_response_by_categraf.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "name": "网络地址探活失败", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "net_response_result_code != 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] \ No newline at end of file diff --git a/etc/alerts/ntp_by_categraf.json b/etc/alerts/ntp_by_categraf.json deleted file mode 100644 index 2ab938894cd30c6fc5a6de0bc51921a0351bee09..0000000000000000000000000000000000000000 --- a/etc/alerts/ntp_by_categraf.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "name": "NTP时间偏移太大", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "ntp_offset_ms > 1000 or ntp_offset_ms < -1000", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] \ No newline at end of file diff --git a/etc/alerts/ping_by_categraf.json b/etc/alerts/ping_by_categraf.json deleted file mode 100644 index 85d02d64723533d68317da19b105d74196297240..0000000000000000000000000000000000000000 --- a/etc/alerts/ping_by_categraf.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "name": "PING地址探测失败", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "ping_result_code != 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] \ No newline at end of file diff --git a/etc/alerts/process_by_exporter.json b/etc/alerts/process_by_exporter.json deleted file mode 100644 index b1c724b656092c4029d4d4b48c9983cfb35d2488..0000000000000000000000000000000000000000 --- a/etc/alerts/process_by_exporter.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "name": "Process X high number of open files", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "avg by (instance) (namedprocess_namegroup_worst_fd_ratio{groupname=\"X\"}) * 100 > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ProcessHighOpenFiles" - ] - }, - { - "name": "Process X is down", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "sum by (instance) (namedprocess_namegroup_num_procs{groupname=\"X\"}) == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ProcessNotRunning" - ] - }, - { - "name": "Process X is restarted", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "namedprocess_namegroup_oldest_start_time_seconds{groupname=\"X\"} > time() - 60 ", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=ProcessRestarted" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/procstat_by_categraf.json b/etc/alerts/procstat_by_categraf.json deleted file mode 100644 index 351bf6b9b9a6b5e7233c26a7c47cb8da3799a35e..0000000000000000000000000000000000000000 --- a/etc/alerts/procstat_by_categraf.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "name": "进程监控-有进程数为0,某进程可能挂了", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "procstat_lookup_count == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "进程监控-进程句柄限制过小", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "procstat_rlimit_num_fds_soft < 2048", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "notify_recovered": 1, - "notify_channels": [ - "email", - "dingtalk", - "wecom" - ], - "notify_repeat_step": 60, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } -] \ No newline at end of file diff --git a/etc/alerts/redis_by_categraf.json b/etc/alerts/redis_by_categraf.json deleted file mode 100644 index aeb8bea9d40d191c2613d145bfafce666bf0ca77..0000000000000000000000000000000000000000 --- a/etc/alerts/redis_by_categraf.json +++ /dev/null @@ -1,182 +0,0 @@ -[ - { - "name": "Redis Ping 延迟高(大于100毫秒)", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "redis_ping_use_seconds > 0.1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=HighPingLatency" - ] - }, - { - "name": "Redis内存使用率较高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "redis_maxmemory > 0 and (redis_used_memory / redis_maxmemory) > 0.85", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisHighMemoryUsage" - ] - }, - { - "name": "Redis出现拒绝连接", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "(rate(redis_rejected_connections[5m])) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisRejectedConnHigh" - ] - }, - { - "name": "Redis刚刚有重启,请注意", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "redis_uptime_in_seconds < 600", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisLowUptime" - ] - }, - { - "name": "Redis较低的命中率", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "rate(redis_keyspace_hits[5m])\n/\n(rate(redis_keyspace_misses[5m]) + rate(redis_keyspace_hits[5m]))\n< 0.9", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisLowHitRatio" - ] - }, - { - "name": "Redis驱逐率较高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "(sum(rate(redis_evicted_keys[5m])) / sum(redis_keyspace_keys)) > 0.1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisHighKeysEvictionRatio" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/redis_by_exporter.json b/etc/alerts/redis_by_exporter.json deleted file mode 100644 index 8e78c112c14bbac7810abac2ea434d6a9e0d17cf..0000000000000000000000000000000000000000 --- a/etc/alerts/redis_by_exporter.json +++ /dev/null @@ -1,212 +0,0 @@ -[ - { - "name": "Redis内存使用率较高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "redis_memory_max_bytes > 0 and (redis_memory_used_bytes / redis_memory_max_bytes) > 0.85", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisHighMemoryUsage" - ] - }, - { - "name": "Redis出现拒绝连接", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "(rate(redis_rejected_connections_total[5m])) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisRejectedConnHigh" - ] - }, - { - "name": "Redis刚刚有重启,请注意", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 0, - "prom_ql": "redis_uptime_in_seconds < 600", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisLowUptime" - ] - }, - { - "name": "Redis客户端连接数过高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "(redis_connected_clients / redis_config_maxclients) > 0.85", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisHighClientsUsage" - ] - }, - { - "name": "Redis延迟高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "sum(rate(redis_commands_duration_seconds_total[5m])) / sum(rate(redis_commands_processed_total[5m])) > 0.25", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisHighResponseTime" - ] - }, - { - "name": "Redis较低的命中率", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "rate(redis_keyspace_hits_total[5m])\n/\n(rate(redis_keyspace_misses_total[5m]) + rate(redis_keyspace_hits_total[5m]))\n< 0.9", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisLowHitRatio" - ] - }, - { - "name": "Redis驱逐率较高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "(sum(rate(redis_evicted_keys_total[5m])) / sum(redis_db_keys)) > 0.1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=RedisHighKeysEvictionRatio" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/windows_by_exporter.json b/etc/alerts/windows_by_exporter.json deleted file mode 100644 index 4d192d08b987973903d686407cc9a554c4a15785..0000000000000000000000000000000000000000 --- a/etc/alerts/windows_by_exporter.json +++ /dev/null @@ -1,182 +0,0 @@ -[ - { - "name": "CPU利用率高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "100 * sum by (instance) (rate(windows_cpu_time_total{mode != 'idle'}[5m])) / count by (instance) (windows_cpu_core_frequency_mhz) > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=HighCPUUsage" - ] - }, - { - "name": "机器在15分钟内发生过重启", - "note": "", - "severity": 3, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "time() - windows_system_system_up_time < 900", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=UpTimeLessThan15Min" - ] - }, - { - "name": "物理内存使用率过高", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "100 * (windows_cs_physical_memory_bytes - windows_os_physical_memory_free_bytes) / windows_cs_physical_memory_bytes > 80", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=HighPhysicalMemoryUsage" - ] - }, - { - "name": "硬盘快要写满了,注意", - "note": "", - "severity": 1, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "100 * (windows_logical_disk_size_bytes - windows_logical_disk_free_bytes) / windows_logical_disk_size_bytes > 90", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=LogicalDiskFull" - ] - }, - { - "name": "系统有In方向丢包情况出现", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "100 * rate(windows_net_packets_received_errors[5m]) / (rate(windows_net_packets_received_errors[5m]) + rate(windows_net_packets_received_total[5m])>0) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=HighInboundErrorRate" - ] - }, - { - "name": "系统有Out方向丢包情况出现", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "100 * rate(windows_net_packets_outbound_errors[5m]) / (rate(windows_net_packets_outbound_errors[5m]) + rate(windows_net_packets_sent_total[5m])>0) > 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [ - "alertname=HighOutboundErrorRate" - ] - } - ] \ No newline at end of file diff --git a/etc/alerts/zookeeper_by_exporter.json b/etc/alerts/zookeeper_by_exporter.json deleted file mode 100644 index aa7cc9cfd4300bfbfa13e3d7e69d54665f102ecd..0000000000000000000000000000000000000000 --- a/etc/alerts/zookeeper_by_exporter.json +++ /dev/null @@ -1,114 +0,0 @@ -[ - { - "name": "Zookeeper leader 个数大于1", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "sum(zk_server_leader) > 1", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "Zookeeper 实例运行异常", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "zk_ruok == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "Zookeeper 没有 leader 了", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "sum(zk_server_leader) == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - }, - { - "name": "Zookeeper 挂掉了", - "note": "", - "severity": 2, - "disabled": 0, - "prom_for_duration": 60, - "prom_ql": "zk_up == 0", - "prom_eval_interval": 15, - "enable_stime": "00:00", - "enable_etime": "23:59", - "enable_days_of_week": [ - "1", - "2", - "3", - "4", - "5", - "6", - "0" - ], - "enable_in_bg": 0, - "notify_recovered": 1, - "notify_channels": [], - "notify_repeat_step": 60, - "recover_duration": 0, - "callbacks": [], - "runbook_url": "", - "append_tags": [] - } - ] \ No newline at end of file diff --git a/etc/webapi.conf b/etc/config.toml similarity index 63% rename from etc/webapi.conf rename to etc/config.toml index 969aa64e77901f302a683e76f26d3bc3c3aa21d5..e07ae1deb524147e7ec2e3242b6be2589848be45 100644 --- a/etc/webapi.conf +++ b/etc/config.toml @@ -1,79 +1,6 @@ -# debug, release +[Global] RunMode = "release" -# # custom i18n dict config -# I18N = "./etc/i18n.json" - -# # custom i18n request header key -# I18NHeaderKey = "X-Language" - -# metrics descriptions -MetricsYamlFile = "./etc/metrics.yaml" - -BuiltinAlertsDir = "./etc/alerts" -BuiltinDashboardsDir = "./etc/dashboards" - -# config | api -ClustersFrom = "config" - -# using when ClustersFrom = "api" -ClustersFromAPIs = [] - -[[NotifyChannels]] -Label = "邮箱" -# do not change Key -Key = "email" - -[[NotifyChannels]] -Label = "钉钉机器人" -# do not change Key -Key = "dingtalk" - -[[NotifyChannels]] -Label = "企微机器人" -# do not change Key -Key = "wecom" - -[[NotifyChannels]] -Label = "飞书机器人" -# do not change Key -Key = "feishu" - -[[NotifyChannels]] -Label = "mm bot" -# do not change Key -Key = "mm" - -[[NotifyChannels]] -Label = "telegram机器人" -# do not change Key -Key = "telegram" - -[[ContactKeys]] -Label = "Wecom Robot Token" -# do not change Key -Key = "wecom_robot_token" - -[[ContactKeys]] -Label = "Dingtalk Robot Token" -# do not change Key -Key = "dingtalk_robot_token" - -[[ContactKeys]] -Label = "Feishu Robot Token" -# do not change Key -Key = "feishu_robot_token" - -[[ContactKeys]] -Label = "MatterMost Webhook URL" -# do not change Key -Key = "mm_webhook_url" - -[[ContactKeys]] -Label = "Telegram Robot Token" -# do not change Key -Key = "telegram_robot_token" - [Log] # log write dir Dir = "logs" @@ -88,19 +15,54 @@ Output = "stdout" # # unit: MB # RotateSize = 256 +[DB] +# postgres: host=%s port=%s user=%s dbname=%s password=%s sslmode=%s +DSN="root:Qzbr6EGAvVNkA7RF@tcp(10.206.0.16:3306)/n9e_v6?charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true" +# enable debug mode or not +Debug = false +# mysql postgres +DBType = "mysql" +# unit: s +MaxLifetime = 7200 +# max open connections +MaxOpenConns = 150 +# max idle connections +MaxIdleConns = 50 +# table prefix +TablePrefix = "" +# enable auto migrate or not +# EnableAutoMigrate = false + +[Redis] +# address, ip:port or ip1:port,ip2:port for cluster and sentinel(SentinelAddrs) +Address = "10.206.0.16:6379" +# Username = "" +# Password = "" +# DB = 0 +# UseTLS = false +# TLSMinVersion = "1.2" +# standalone cluster sentinel +RedisType = "standalone" +# Mastername for sentinel type +# MasterName = "mymaster" +# SentinelUsername = "" +# SentinelPassword = "" + [HTTP] # http listening address Host = "0.0.0.0" # http listening port -Port = 18000 +Port = 17000 # https cert file path CertFile = "" # https key file path KeyFile = "" # whether print access log -PrintAccessLog = true +PrintAccessLog = false # whether enable pprof PProf = false +# expose prometheus /metrics? +ExposeMetrics = true # http graceful shutdown timeout, unit: s ShutdownTimeout = 30 # max content length: 64M @@ -112,7 +74,40 @@ WriteTimeout = 40 # http server idle timeout, unit: s IdleTimeout = 120 -[JWTAuth] +# [HTTP.BasicAuth] +# username = "password" + +[Alert] +[Alert.Heartbeat] +# auto detect if blank +IP = "" +# unit ms +Interval = 1000 +ClusterName = "default" + +[Alert.Alerting] +# timeout settings, unit: ms, default: 30000ms +Timeout=30000 +NotifyConcurrency = 10 +[Alert.Alerting.CallScript] +# built in sending capability in go code +# so, no need enable script sender +Enable = false +ScriptPath = "./etc/script/notify.py" + +[[Alert.Alerting.Webhooks]] +Enable = false +Url = "http://a.com/n9e/callback" +BasicAuthUser = "" +BasicAuthPass = "" +Timeout = "5s" +Headers = ["Content-Type", "application/json", "X-From", "N9E"] + +[Center] +MetricsYamlFile = "./etc/metrics.yaml" +I18NHeaderKey = "X-Language" + +[Center.JWTAuth] # signing key SigningKey = "5b94a0fd640fe2765af826acfe42d151" # unit: min @@ -121,21 +116,35 @@ AccessExpired = 1500 RefreshExpired = 10080 RedisKeyPrefix = "/jwt/" -[ProxyAuth] +[Center.ProxyAuth] # if proxy auth enabled, jwt auth is disabled Enable = false # username key in http proxy header HeaderUserNameKey = "X-User-Name" DefaultRoles = ["Standard"] -[BasicAuth] +[Center.BasicAuth] user001 = "ccc26da7b9aba533cbb263a36c07dcc5" -[AnonymousAccess] -PromQuerier = false -AlertDetail = false +[Center.AnonymousAccess] +PromQuerier = true +AlertDetail = true + +[Center.Ibex] +Address = "http://127.0.0.1:10090" +# basic auth +BasicAuthUser = "ibex" +BasicAuthPass = "ibex" +# unit: ms +Timeout = 3000 -[LDAP] +[Center.TargetMetrics] +TargetUp = '''max(max_over_time(target_up{ident=~"(%s)"}[%dm])) by (ident)''' +LoadPerCore = '''max(max_over_time(system_load_norm_1{ident=~"(%s)"}[%dm])) by (ident)''' +MemUtil = '''100-max(max_over_time(mem_available_percent{ident=~"(%s)"}[%dm])) by (ident)''' +DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)''' + +[Center.LDAP] Enable = false Host = "ldap.example.org" Port = 389 @@ -152,12 +161,12 @@ StartTLS = true # ldap user default roles DefaultRoles = ["Standard"] -[LDAP.Attributes] +[Center.LDAP.Attributes] Nickname = "cn" Phone = "mobile" Email = "mail" -[OIDC] +[Center.OIDC] Enable = false DisplayName = "OIDC登录" RedirectURL = "http://n9e.com/callback" @@ -167,12 +176,12 @@ ClientSecret = "" CoverAttributes = true DefaultRoles = ["Standard"] -[OIDC.Attributes] +[Center.OIDC.Attributes] Nickname = "nickname" Phone = "phone_number" Email = "email" -[CAS] +[Center.CAS] Enable = false DisplayName = "CAS登录" SsoAddr = "https://cas.example.com/cas/" @@ -181,12 +190,12 @@ CoverAttributes = false # cas user default roles DefaultRoles = ["Standard"] -[CAS.Attributes] +[Center.CAS.Attributes] Nickname = "nickname" Phone = "phone_number" Email = "email" -[OAuth] +[Center.OAuth] Enable = false DisplayName = "OAuth2登录" RedirectURL = "http://127.0.0.1:18000/callback/oauth" @@ -203,80 +212,60 @@ UserinfoIsArray = false UserinfoPrefix = "data" Scopes = ["profile", "email", "phone"] -[OAuth.Attributes] +[Center.OAuth.Attributes] # Username must be defined Username = "username" Nickname = "nickname" Phone = "phone_number" Email = "email" -# example -# # nested : UserinfoIsArray=false, UserinfoPrefix="data" -# # {"data":{"username":"123456","nickname":"姓名"},"code":0,"message":"ok"} -# # nested and array : UserinfoIsArray=true, UserinfoPrefix="data" -# # {"data":[{"username":"123456","nickname":"姓名"}],"code":0,"message":"ok"} -# # flat : UserinfoIsArray=false, UserinfoPrefix="" -# # {"username":"123456","nickname":"姓名"} -# # flat and array : UserinfoIsArray=true, UserinfoPrefix="" -# # [{"username":"123456","nickname":"姓名"}] - -[Redis] -# address, ip:port or ip1:port,ip2:port for cluster and sentinel(SentinelAddrs) -Address = "127.0.0.1:6379" -# Username = "" -# Password = "" -# DB = 0 -# UseTLS = false -# TLSMinVersion = "1.2" -# standalone cluster sentinel -RedisType = "standalone" -# Mastername for sentinel type -# MasterName = "mymaster" -# SentinelUsername = "" -# SentinelPassword = "" - -[DB] -DSN="root:1234@tcp(127.0.0.1:3306)/n9e_v5?charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true" -# enable debug mode or not -Debug = false -# mysql postgres -DBType = "mysql" -# unit: s -MaxLifetime = 7200 -# max open connections -MaxOpenConns = 150 -# max idle connections -MaxIdleConns = 50 -# table prefix -TablePrefix = "" -# enable auto migrate or not -# EnableAutoMigrate = false - -[[Clusters]] -# Prometheus cluster name -Name = "Default" -# Prometheus APIs base url -Prom = "http://127.0.0.1:9090" +[Pushgw] +DatasourceId = 3 +# use target labels in database instead of in series +LabelRewrite = true +# # default busigroup key name +# BusiGroupLabelKey = "busigroup" +# ForceUseServerTS = false + +# [Pushgw.DebugSample] +# ident = "xx" +# __name__ = "xx" + +# [Pushgw.WriterOpt] +# # Writer Options +# QueueCount = 1000 +# QueueMaxSize = 1000000 +# QueuePopSize = 1000 +# # ident or metric +# ShardingKey = "ident" + +[[Pushgw.Writers]] +Url = "http://10.206.0.13:8480/insert/0/prometheus/api/v1/write" # Basic auth username BasicAuthUser = "" # Basic auth password BasicAuthPass = "" # timeout settings, unit: ms -Timeout = 30000 +Headers = ["X-From", "n9e"] +Timeout = 10000 DialTimeout = 3000 +TLSHandshakeTimeout = 30000 +ExpectContinueTimeout = 1000 +IdleConnTimeout = 90000 +# time duration, unit: ms +KeepAlive = 30000 +MaxConnsPerHost = 0 +MaxIdleConns = 100 MaxIdleConnsPerHost = 100 -Headers = ["X-From", "n9e"] - -[Ibex] -Address = "http://127.0.0.1:10090" -# basic auth -BasicAuthUser = "ibex" -BasicAuthPass = "ibex" -# unit: ms -Timeout = 3000 - -[TargetMetrics] -TargetUp = '''max(max_over_time(target_up{ident=~"(%s)"}[%dm])) by (ident)''' -LoadPerCore = '''max(max_over_time(system_load_norm_1{ident=~"(%s)"}[%dm])) by (ident)''' -MemUtil = '''100-max(max_over_time(mem_available_percent{ident=~"(%s)"}[%dm])) by (ident)''' -DiskUtil = '''max(max_over_time(disk_used_percent{ident=~"(%s)", path="/"}[%dm])) by (ident)''' +## Optional TLS Config +# UseTLS = false +# TLSCA = "/etc/n9e/ca.pem" +# TLSCert = "/etc/n9e/cert.pem" +# TLSKey = "/etc/n9e/key.pem" +# InsecureSkipVerify = false +# [[Writers.WriteRelabels]] +# Action = "replace" +# SourceLabels = ["__address__"] +# Regex = "([^:]+)(?::\\d+)?" +# Replacement = "$1:80" +# TargetLabel = "__address__" \ No newline at end of file diff --git a/etc/dashboards/elasticsearch_by_exporter.json b/etc/dashboards/elasticsearch_by_exporter.json deleted file mode 100644 index b2875944eb09b80416683469ef51d17149581630..0000000000000000000000000000000000000000 --- a/etc/dashboards/elasticsearch_by_exporter.json +++ /dev/null @@ -1,431 +0,0 @@ -[ - { - "name": "ElasticSearch - 模板", - "tags": "Prometheus ElasticSearch ES", - "configs": "{\"var\":[{\"name\":\"cluster\",\"definition\":\"label_values(elasticsearch_indices_docs,cluster)\",\"options\":[\"elasticsearch-cluster\"]},{\"name\":\"instance\",\"definition\":\"label_values(elasticsearch_indices_docs{cluster=\\\"$cluster\\\", name!=\\\"\\\"},instance)\",\"options\":[\"10.206.0.7:9200\"]},{\"name\":\"name\",\"definition\":\"label_values(elasticsearch_indices_docs{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\", name!=\\\"\\\"},name)\",\"options\":[\"node-2\",\"node-1\",\"node-3\"],\"multi\":true,\"allOption\":true}]}", - "chart_groups": [ - { - "name": "KPI", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_number_of_nodes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Data Nodes\",\"description\":\"集群数据节点数量\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":6,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_number_of_nodes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Nodes\",\"description\":\"集群节点数量\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":3,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum (elasticsearch_process_cpu_percent{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"} ) / count (elasticsearch_process_cpu_percent{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"} )\"}],\"name\":\"CPU usage Avg\",\"description\":\"平均CPU使用率\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"special\":80,\"from\":80},\"result\":{\"color\":\"#f90606\"}},{\"type\":\"range\",\"match\":{\"special\":70,\"from\":70},\"result\":{\"color\":\"#f5ac0f\"}},{\"type\":\"range\",\"match\":{\"to\":70},\"result\":{\"color\":\"#21c00c\"}}],\"standardOptions\":{\"util\":\"percent\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":9,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum (elasticsearch_jvm_memory_used_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}) / sum (elasticsearch_jvm_memory_max_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}) * 100\"}],\"name\":\"JVM memory used Avg\",\"description\":\"平均JVM内存使用率\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"special\":80,\"from\":80},\"result\":{\"color\":\"#f12c09\"}},{\"type\":\"range\",\"match\":{\"from\":70,\"to\":80},\"result\":{\"color\":\"#fbca18\"}},{\"type\":\"range\",\"match\":{\"to\":70},\"result\":{\"color\":\"#21c00c\"}}],\"standardOptions\":{\"util\":\"percent\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":12,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum (elasticsearch_process_open_files_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"})\"}],\"name\":\"Open file descriptors\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":15,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(elasticsearch_breakers_tripped{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"})\"}],\"name\":\"Tripped for breakers\",\"description\":\"集群断路器阻断总数\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"result\":{\"text\":\"\",\"color\":\"#21c00c\"},\"match\":{\"special\":0}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":18,\"y\":0,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_number_of_pending_tasks{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Pending tasks\",\"description\":\"等待执行的集群变更任务数量\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":5},\"result\":{\"color\":\"#f24207\"}},{\"type\":\"range\",\"match\":{\"from\":1,\"to\":5},\"result\":{\"color\":\"#f9a006\"}},{\"type\":\"range\",\"match\":{\"to\":1},\"result\":{\"color\":\"#21c00c\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":21,\"y\":0,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_status{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",color=\\\"red\\\"}==1 or (elasticsearch_cluster_health_status{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",color=\\\"green\\\"}==1)+4 or (elasticsearch_cluster_health_status{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",color=\\\"yellow\\\"}==1)+2\"}],\"name\":\"Cluster health\",\"description\":\"绿色:健康,黄色:存在副本分片异常,红色:存在主分片异常\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"result\":{\"text\":\"N/A\"},\"match\":{}},{\"type\":\"special\",\"match\":{\"special\":5},\"result\":{\"text\":\"Green\",\"color\":\"#21c00c\"}},{\"type\":\"special\",\"match\":{\"special\":3},\"result\":{\"text\":\"Yellow\",\"color\":\"#f9e406\"}},{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"text\":\"Red\",\"color\":\"#f43606\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":0,\"y\":0,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "Shards", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_active_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Active shards\",\"description\":\"活跃分片数\\nAggregate total of all shards across all indices, which includes replica shards\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_active_primary_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Active primary shards\",\"description\":\"活跃主分片数\\nThe number of primary shards in your cluster. This is an aggregate total across all indices.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":4,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_initializing_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Initializing shards\",\"description\":\"创建中分片数量\\nCount of shards that are being freshly created\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":8,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_relocating_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Relocating shards\",\"description\":\"迁移中分片数量\\nThe number of shards that are currently moving from one node to another node.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_unassigned_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Unassigned shards\",\"description\":\"未分配的分片数量\\nThe number of shards that exist in the cluster state, but cannot be found in the cluster itself\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_cluster_health_delayed_unassigned_shards{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\"}\"}],\"name\":\"Delayed shards\",\"description\":\"暂缓分配的分片数量,当节点丢失,会发生选主和数据拷贝。为了较少网络抖动等原因导致的重分配情况,配置delay参数,该值为等待delay到期将被重分配的分片数量\\nShards delayed to reduce reallocation overhead\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":0,\"i\":\"5\"}}", - "weight": 0 - } - ] - }, - { - "name": "JVM Garbage Collection", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_jvm_gc_collection_seconds_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}} - {{gc}}\"}],\"name\":\"GC count\",\"description\":\"GC运行次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_jvm_gc_collection_seconds_sum{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}} - {{gc}}\"}],\"name\":\"GC time\",\"description\":\"GC运行耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Translog", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_translog_operations{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Total translog operations\",\"description\":\"translog大小(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_translog_size_in_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Total translog size in bytes\",\"description\":\"translog大小(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Breakers", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_breakers_tripped{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{breaker}}\"}],\"name\":\"Tripped for breakers\",\"description\":\"节点断路器阻断总数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_breakers_estimated_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{breaker}}\"},{\"expr\":\"elasticsearch_breakers_limit_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"{{name}}: limit for {{breaker}}\"}],\"name\":\"Estimated size in bytes of breaker\",\"description\":\"预估内存大小和限制内存大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Cpu and Memory", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_os_load1{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"load1: {{name}}\"},{\"expr\":\"elasticsearch_os_load5{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"load5: {{name}}\"},{\"expr\":\"elasticsearch_os_load15{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"C\",\"legend\":\"load15: {{name}}\"}],\"name\":\"Load average\",\"description\":\"1m/5m/15m系统负载\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_process_cpu_percent{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"CPU usage\",\"description\":\"进程CPU占比\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_jvm_memory_used_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}} used: {{area}}\"},{\"expr\":\"elasticsearch_jvm_memory_max_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"{{name}} max: {{area}}\"},{\"expr\":\"elasticsearch_jvm_memory_pool_peak_used_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"C\",\"legend\":\"{{name}} peak used pool: {{pool}}\"}],\"name\":\"JVM memory usage\",\"description\":\"进程内存占用/限制/峰值(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_jvm_memory_committed_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}} committed: {{area}}\"},{\"expr\":\"elasticsearch_jvm_memory_max_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"refId\":\"B\",\"legend\":\"{{name}} max: {{area}}\"}],\"name\":\"JVM memory committed\",\"description\":\"JVM申请/限制内存(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Disk and Network", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"1-(elasticsearch_filesystem_data_available_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}/elasticsearch_filesystem_data_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"})\",\"legend\":\"{{name}}: {{path}}\"}],\"name\":\"Disk usage\",\"description\":\"磁盘使用率\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_transport_tx_size_bytes_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: sent\"},{\"expr\":\"-irate(elasticsearch_transport_rx_size_bytes_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{name}}: received\"}],\"name\":\"Network usage\",\"description\":\"网络流量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Documents", - "weight": 7, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_docs{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Documents count on node\",\"description\":\"节点文档总数,不包含已删除文档和子文档以及刚索引的文档\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_indexing_index_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents indexed rate\",\"description\":\"平均每秒索引文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_docs_deleted{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents deleted rate\",\"description\":\"平均每秒删除文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_merges_docs_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents merged rate\",\"description\":\"平均每秒合并文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_merges_total_size_bytes_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Documents merged bytes\",\"description\":\"平均每秒合并文档大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "Times", - "weight": 8, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_search_query_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Query time\",\"description\":\"查询操作耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_indexing_index_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Indexing time\",\"description\":\"索引操作耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_merges_total_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Merging time\",\"description\":\"合并操作耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_store_throttle_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Throttle time for index store\",\"description\":\"索引存储限制耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Total Operations states", - "weight": 9, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_indices_indexing_index_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: indexing\"},{\"expr\":\"irate(elasticsearch_indices_search_query_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{name}}: query\"},{\"expr\":\"irate(elasticsearch_indices_search_fetch_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"C\",\"legend\":\"{{name}}: fetch\"},{\"expr\":\"irate(elasticsearch_indices_merges_total_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"D\",\"legend\":\"{{name}}: merges\"},{\"expr\":\"irate(elasticsearch_indices_refresh_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"E\",\"legend\":\"{{name}}: refresh\"},{\"expr\":\"irate(elasticsearch_indices_flush_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"F\",\"legend\":\"{{name}}: flush\"},{\"expr\":\"irate(elasticsearch_indices_get_exists_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"G\",\"legend\":\"{{name}}: get_exists\"},{\"expr\":\"irate(elasticsearch_indices_get_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"H\",\"legend\":\"{{name}}: get_time\"},{\"expr\":\"irate(elasticsearch_indices_get_missing_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"I\",\"legend\":\"{{name}}: get_missing\"},{\"expr\":\"irate(elasticsearch_indices_indexing_delete_time_seconds_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"K\",\"legend\":\"{{name}}: indexing_delete\"},{\"expr\":\"irate(elasticsearch_indices_get_time_seconds{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"L\",\"legend\":\"{{name}}: get\"}],\"name\":\"Total Operations time\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"seconds\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_indexing_index_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: indexing\"},{\"expr\":\"rate(elasticsearch_indices_search_query_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{name}}: query\"},{\"expr\":\"rate(elasticsearch_indices_search_fetch_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"C\",\"legend\":\"{{name}}: fetch\"},{\"expr\":\"rate(elasticsearch_indices_merges_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"D\",\"legend\":\"{{name}}: merges\"},{\"expr\":\"rate(elasticsearch_indices_refresh_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"E\",\"legend\":\"{{name}}: refresh\"},{\"expr\":\"rate(elasticsearch_indices_flush_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"F\",\"legend\":\"{{name}}: flush\"},{\"expr\":\"rate(elasticsearch_indices_get_exists_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"G\",\"legend\":\"{{name}}: get_exists\"},{\"expr\":\"rate(elasticsearch_indices_get_missing_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"H\",\"legend\":\"{{name}}: get_missing\"},{\"expr\":\"rate(elasticsearch_indices_get_tota{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"I\",\"legend\":\"{{name}}: get\"},{\"expr\":\"rate(elasticsearch_indices_indexing_delete_total{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"refId\":\"K\",\"legend\":\"{{name}}: indexing_delete\"}],\"name\":\"Total Operations rate\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Thread Pool", - "weight": 10, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_thread_pool_rejected_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool operations rejected\",\"description\":\"线程池reject次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_thread_pool_active_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool threads active\",\"description\":\"活跃线程数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_thread_pool_queue_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool threads queued\",\"description\":\"排队等待线程任务数量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"irate(elasticsearch_thread_pool_completed_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}: {{ type }}\"}],\"name\":\"Thread Pool operations completed\",\"description\":\"线程池complete次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Caches", - "weight": 11, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_fielddata_memory_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Field data memory size\",\"description\":\"fielddata cache内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_fielddata_evictions{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Field data evictions\",\"description\":\"fielddata cache平均每秒内存剔除次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_query_cache_memory_size_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Query cache size\",\"description\":\"query cache内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_query_cache_evictions{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Query cache evictions\",\"description\":\"query cache平均每秒内存剔除次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(elasticsearch_indices_filter_cache_evictions{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}[5m])\",\"legend\":\"{{name}}\"}],\"name\":\"Evictions from filter cache\",\"description\":\"老版本的filter cache内存剔除次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "Segments", - "weight": 12, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segments_count{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Count of index segments\",\"description\":\"segment个数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segments_memory_bytes{instance=\\\"$instance\\\",cluster=\\\"$cluster\\\",name=~\\\"$name\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Current memory size of segments in bytes\",\"description\":\"segment内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Count of documents and Total size", - "weight": 13, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_docs_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Count of documents with only primary shards\",\"description\":\"主分片文档数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"stack\":\"off\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_store_size_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Total size of stored index data in bytes with only primary shards on all nodes\",\"description\":\"主分片索引容量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_store_size_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Total size of stored index data in bytes with all shards on all nodes\",\"description\":\"所有分片索引容量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":4,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Index writer", - "weight": 14, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_index_writer_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Index writer with only primary shards on all nodes in bytes\",\"description\":\"主分片索引写入数据量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_index_writer_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Index writer with all shards on all nodes in bytes\",\"description\":\"所有分片索引写入数据量(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Segments", - "weight": 15, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_count_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Segments with only primary shards on all nodes\",\"description\":\"主分片segment数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_count_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Segments with all shards on all nodes\",\"description\":\"所有分片segment总数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of segments with only primary shards on all nodes in bytes\",\"description\":\"主分片segment容量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":4,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of segments with all shards on all nodes in bytes\",\"description\":\"所有分片segment容量\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":6,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Doc values", - "weight": 16, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_doc_values_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Doc values with only primary shards on all nodes in bytes\",\"description\":\"主分片doc value内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_doc_values_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Doc values with all shards on all nodes in bytes\",\"description\":\"所有分片doc value内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Fields", - "weight": 17, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fields_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fields with only primary shards on all nodes in bytes\",\"description\":\"分片field内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fields_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fields with all shards on all nodes in bytes\",\"description\":\"所有分片field内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Fixed bit", - "weight": 18, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fixed_bit_set_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fixed bit with only primary shards on all nodes in bytes\",\"description\":\"主分片fixed bit set内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_fixed_bit_set_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of fixed bit with all shards on all nodes in bytes\",\"description\":\"所有分片fixed bit set内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Norms", - "weight": 19, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_norms_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of norms with only primary shards on all nodes in bytes\",\"description\":\"主分片normalization factor内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_norms_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of norms with all shards on all nodes in bytes\",\"description\":\"所有分片normalization factor内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Points", - "weight": 20, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_points_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of points with only primary shards on all nodes in bytes\",\"description\":\"主分片point内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_points_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of points with all shards on all nodes in bytes\",\"description\":\"所有分片point内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Terms", - "weight": 21, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_terms_memory_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of terms with only primary shards on all nodes in bytes\",\"description\":\"主分片term内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_terms_memory_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Number of terms with all shards on all nodes in bytes\",\"description\":\"所有分片term内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Indices: Terms", - "weight": 22, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_version_map_memory_bytes_primary{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of version map with only primary shards on all nodes in bytes\",\"description\":\"所有分片version map内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"elasticsearch_indices_segment_version_map_memory_bytes_total{instance=~\\\"$instance\\\"}\",\"legend\":\"{{index}}\"}],\"name\":\"Size of version map with all shards on all nodes in bytes\",\"description\":\"所有分片version map内存占用(byte)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/http_response_by_categraf.json b/etc/dashboards/http_response_by_categraf.json deleted file mode 100644 index a77b0fb5ca72bee94711a82e276e978c42122a05..0000000000000000000000000000000000000000 --- a/etc/dashboards/http_response_by_categraf.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "HTTP探测", - "tags": "", - "configs": "", - "chart_groups": [ - { - "name": "Default chart group", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"max(http_response_result_code) by (target)\",\"legend\":\"UP?\"},{\"expr\":\"max(http_response_response_code) by (target)\",\"refId\":\"B\",\"legend\":\"status code\"},{\"expr\":\"max(http_response_response_time) by (target)\",\"refId\":\"C\",\"legend\":\"latency(s)\"},{\"expr\":\"max(http_response_cert_expire_timestamp) by (target) - time()\",\"refId\":\"D\",\"legend\":\"cert expire\"}],\"name\":\"URL Details\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelValuesToRows\",\"aggrDimension\":\"target\"},\"options\":{\"valueMappings\":[],\"standardOptions\":{}},\"overrides\":[{\"properties\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"UP\",\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"special\":1,\"from\":1},\"result\":{\"text\":\"DOWN\",\"color\":\"#e90f0f\"}}],\"standardOptions\":{}},\"matcher\":{\"value\":\"A\"}},{\"type\":\"special\",\"matcher\":{\"value\":\"D\"},\"properties\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":4,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } -] \ No newline at end of file diff --git a/etc/dashboards/jmx_by_exporter.json b/etc/dashboards/jmx_by_exporter.json deleted file mode 100644 index 79ca038930922669bc6dd3e9fe3ac5617403c857..0000000000000000000000000000000000000000 --- a/etc/dashboards/jmx_by_exporter.json +++ /dev/null @@ -1,125 +0,0 @@ -[ - { - "name": "JMX - 模板", - "tags": "Prometheus JMX", - "configs": "{\"var\":[{\"name\":\"job\",\"definition\":\"jmx_exporter\"},{\"name\":\"instance\",\"definition\":\"label_values(jmx_scrape_error,instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"up{job=\\\"$job\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Status\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"text\":\"UP\",\"color\":\"#1eac02\"}},{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"DOWN\",\"color\":\"#f00a0a\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"time() - process_start_time_seconds{job=\\\"$job\\\",instance=\\\"$instance\\\"}\"}],\"name\":\"Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"os_available_processors{job=\\\"$job\\\",instance=\\\"$instance\\\"}\"}],\"name\":\"Available CPUs\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"os_open_file_descriptor_count{job=\\\"$job\\\",instance=\\\"$instance\\\"}\"}],\"name\":\"Open file descriptors\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "JVM Memory", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_bytes_used{area=\\\"heap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_bytes_max{area=\\\"heap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Max\"}],\"name\":\"JVM Memory(heap)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_bytes_used{area=\\\"nonheap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_bytes_max{area=\\\"nonheap\\\",job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Max\"}],\"name\":\"JVM Memory(nonheap)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Memory Pool", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"CodeHeap 'non-nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"CodeHeap 'non-nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"CodeHeap 'non-nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"CodeHeap 'non-nmethods'\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"CodeHeap 'profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"CodeHeap 'profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"CodeHeap 'profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"CodeHeap 'profiled nmethods'\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"CodeHeap 'non-profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"CodeHeap 'non-profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"CodeHeap 'non-profiled nmethods'\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"CodeHeap 'non-profiled nmethods'\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"G1 Eden Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"G1 Eden Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"G1 Eden Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"G1 Eden Space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"Compressed Class Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"Compressed Class Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"Compressed Class Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"Compressed Class Space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"G1 Survivor Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"G1 Survivor Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"G1 Survivor Space\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"G1 Survivor Space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"G1 Old Gen\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"G1 Old Gen\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"G1 Old Gen\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"G1 Old Gen\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_memory_pool_bytes_max{pool=\\\"Metaspace\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Max\"},{\"expr\":\"jvm_memory_pool_bytes_used{pool=\\\"Metaspace\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Used\"},{\"expr\":\"jvm_memory_pool_bytes_committed{pool=\\\"Metaspace\\\", job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Committed\"}],\"name\":\"Metaspace\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":2,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "GC", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"increase(jvm_gc_collection_seconds_sum{job=\\\"$job\\\",instance=~\\\"$instance\\\"}[1m])\"}],\"name\":\"过去一分钟GC耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"increase(jvm_gc_collection_seconds_count{job=\\\"$job\\\",instance=\\\"$instance\\\"}[1m])\",\"legend\":\"\"}],\"name\":\"过去一分钟GC次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"increase(jvm_gc_collection_seconds_sum{job=\\\"$job\\\",instance=\\\"$instance\\\"}[1m])/increase(jvm_gc_collection_seconds_count{job=\\\"$job\\\",instance=\\\"$instance\\\"}[1m])\",\"legend\":\"\"}],\"name\":\"过去一分钟每次GC平均耗时(秒)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"none\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"bars\",\"stack\":\"off\",\"fillOpacity\":1},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Threads and Class loading", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_threads_current{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"current\"},{\"expr\":\"jvm_threads_daemon{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"daemon\"},{\"expr\":\"jvm_threads_deadlocked{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"deadlocked\"}],\"name\":\"Threads\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"jvm_classes_loaded{job=\\\"$job\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Class loading\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Physical memory", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"os_total_physical_memory_bytes{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"legend\":\"Total physical memory\"},{\"expr\":\"os_committed_virtual_memory_bytes{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"Committed virtual memory\"},{\"expr\":\"os_free_physical_memory_bytes{job=\\\"$job\\\",instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"Free physical memory\"}],\"name\":\"Physical memory\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/linux_by_exporter.json b/etc/dashboards/linux_by_exporter.json deleted file mode 100644 index 738823c9110981d6457b65a3a5c2bffeb8300396..0000000000000000000000000000000000000000 --- a/etc/dashboards/linux_by_exporter.json +++ /dev/null @@ -1,223 +0,0 @@ -[ - { - "name": "HOST - 模板", - "tags": "Prometheus Host", - "configs": "{\"var\":[{\"name\":\"node\",\"definition\":\"label_values(node_cpu_seconds_total, instance)\",\"selected\":\"$node\",\"options\":[\"tt-fc-es01.nj:12345\",\"tt-fc-es02.nj:12345\",\"tt-fc-dev01.nj:12345\",\"10.206.0.13:9100\"]}],\"links\":[{\"title\":\"n9e\",\"url\":\"https://n9e.gitee.io/\",\"targetBlank\":true},{\"title\":\"author\",\"url\":\"http://flashcat.cloud/\",\"targetBlank\":true}]}", - "chart_groups": [ - { - "name": "整体概况", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,100-(avg by (mode, instance)(rate(node_cpu_seconds_total{mode=\\\"idle\\\"}[1m])))*100)\",\"legend\":\"{{instance}}\"}],\"name\":\"cpu使用率 top10\",\"links\":[],\"description\":\"\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":9,\"x\":3,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - (node_memory_Cached_bytes + node_memory_Buffers_bytes))/node_memory_MemTotal_bytes*100)\",\"legend\":\"{{instance}}\"}],\"name\":\"内存使用率 top10\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,(node_filesystem_avail_bytes{device!~'rootfs', device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"} * 100) / node_filesystem_size_bytes{device!~'rootfs', device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"})\",\"legend\":\"{{instance}}-{{mountpoint}}\"}],\"name\":\"磁盘分区使用率 top10\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(10,rate(node_disk_io_time_seconds_total[5m]) * 100)\",\"legend\":\"{{instance}}-{{device}}\"}],\"name\":\"设备io util top10\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"count(node_boot_time_seconds)\"}],\"name\":\"监控机器数\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":40}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":0,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "单机概况", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"(node_memory_MemTotal_bytes{instance=\\\"$node\\\"} - node_memory_MemFree_bytes{instance=\\\"$node\\\"} - (node_memory_Cached_bytes{instance=\\\"$node\\\"} + node_memory_Buffers_bytes{instance=\\\"$node\\\"}))/node_memory_MemTotal_bytes{instance=\\\"$node\\\"}*100\"}],\"name\":\"内存使用率\",\"description\":\"如果内存使用率超过50%,则需要扩容或者升级配置了\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":25}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"from\":50,\"to\":100},\"result\":{\"color\":\"#e3170d\"}}],\"standardOptions\":{\"util\":\"percent\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(((count(count(node_cpu_seconds_total{instance=\\\"$node\\\"}) by (cpu))) - avg(sum by (mode)(rate(node_cpu_seconds_total{mode='idle',instance=\\\"$node\\\"}[1m])))) * 100) / count(count(node_cpu_seconds_total{instance=\\\"$node\\\"}) by (cpu))\"}],\"name\":\"CPU使用率\",\"description\":\"如果cpu使用率超过50%,可以通过top命令查看机器上是否有异常进程,如果没有异常进程,则说明服务需要扩容或者机器需要升级配置了\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":30}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"special\":50,\"from\":50,\"to\":100},\"result\":{\"color\":\"#b22222\"}}],\"standardOptions\":{\"util\":\"percent\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"max(100 - ((node_filesystem_avail_bytes{instance=\\\"$node\\\",} * 100) / node_filesystem_size_bytes{instance=\\\"$node\\\"}))\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"磁盘分区使用率最大值\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\",\"decimals\":1},\"thresholds\":{\"steps\":[{\"value\":90,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_time_seconds{instance=\\\"$node\\\"} - node_boot_time_seconds{instance=\\\"$node\\\"}\"}],\"name\":\"启动时长\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"title\":null,\"value\":20}},\"options\":{\"valueMappings\":[],\"standardOptions\":{\"util\":\"humantimeSeconds\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":21,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(node_memory_SwapTotal_bytes{instance=\\\"$node\\\"} - node_memory_SwapFree_bytes{instance=\\\"$node\\\"})\"}],\"name\":\"SWAP内存使用\",\"description\":\"swap使用过高,会影响系统io性能,如果内存够用但swap使用很高,可以调小swappiness的值\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":30}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"special\":50,\"from\":50,\"to\":80},\"result\":{\"color\":\"#fb9b2d\"}},{\"type\":\"range\",\"match\":{\"from\":80,\"to\":100000},\"result\":{\"color\":\"#d10000\"}}],\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":12,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_vmstat_oom_kill{instance=\\\"$node\\\"}[1m])\",\"legend\":\"OOM\"}],\"name\":\"OOM次数\",\"description\":\"大于0,说明有进程内存不够用了,需要考虑扩容或升级配置了\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{\"steps\":[{\"value\":1,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":1,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"max(rate(node_disk_io_time_seconds_total{instance=\\\"$node\\\"}[5m]) * 100)\",\"legend\":\"{{device}}\"}],\"name\":\"磁盘设备io util 最大值\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":1,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filefd_allocated{instance=\\\"$node\\\"}/node_filefd_maximum{instance=\\\"$node\\\"}*100\"}],\"name\":\"FD使用率\",\"description\":\"如果超过80%,建议把文件描述符的最大个数调大,或者扩容\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":25}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"result\":{\"color\":\"#369903\"},\"match\":{\"from\":0,\"to\":50}},{\"type\":\"range\",\"match\":{\"special\":50,\"from\":50,\"to\":80},\"result\":{\"color\":\"#fb9b2d\"}},{\"type\":\"range\",\"match\":{\"from\":80,\"to\":100},\"result\":{\"color\":\"#d10000\"}}],\"standardOptions\":{\"util\":\"percent\",\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":15,\"y\":0,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"max(100 - ((node_filesystem_files_free{instance=\\\"$node\\\",mountpoint!~\\\"/var/lib/.*\\\",mountpoint!~\\\"/run/user.*\\\"} * 100) / node_filesystem_files{instance=\\\"$node\\\",mountpoint!~\\\"/var/lib/.*\\\",mountpoint!~\\\"/run/user.*\\\"}))\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"inode分区使用率最大值\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\",\"decimals\":1},\"thresholds\":{\"steps\":[{\"value\":50,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":1,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(node_filesystem_device_error{instance=\\\"$node\\\",mountpoint!~\\\"/var/lib/.*\\\",mountpoint!~\\\"/run.*\\\"})\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"写文件错误数总和\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":30}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":0,\"to\":0},\"result\":{\"color\":\"#369903\"}},{\"type\":\"range\",\"match\":{\"from\":1,\"to\":10000},\"result\":{\"color\":\"#f0310f\"}}],\"standardOptions\":{\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":3,\"x\":18,\"y\":0,\"i\":\"9\"}}", - "weight": 0 - } - ] - }, - { - "name": "系统指标", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"node_procs_running{instance=\\\"$node\\\"}\",\"legend\":\"{{mountpoint}}\"}],\"name\":\"进程数\",\"description\":\"进程数超过2000,可以考虑扩容了\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[{\"value\":2000,\"color\":\"#ff0000\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_timex_offset_seconds{instance=\\\"$node\\\"}\",\"legend\":\"ntp偏移\"}],\"name\":\"NTP偏移\",\"description\":\"\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_intr_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"Interrupts\"},{\"expr\":\"irate(node_context_switches_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"context switches\"}],\"name\":\"上下文切换/中断\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_entropy_available_bits{instance=\\\"$node\\\"}\",\"legend\":\"entropy\"}],\"name\":\"熵池大小\",\"description\":\"熵池太小 ,程序使用随机函数会阻塞,可以安装 rng-tools 工具增加熵池大小,可参考\\nhttps://codeantenna.com/a/Ab6aMd3NSA \",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[{\"value\":100,\"color\":\"#f70202\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "CPU详情", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\" (avg by (mode)(rate(node_cpu_seconds_total{instance=\\\"$node\\\",mode!=\\\"idle\\\"}[1m])))*100\",\"legend\":\"{{mode}}\"}],\"name\":\"CPU使用率详情\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\" (avg by (mode)(rate(node_cpu_seconds_total{instance=\\\"$node\\\",mode=\\\"idle\\\"}[1m])))*100\",\"legend\":\"cpu_idle\"}],\"name\":\"CPU空闲率\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[{\"value\":10,\"color\":\"#f90101\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_load1{instance=\\\"$node\\\"}\",\"legend\":\"load1\"},{\"expr\":\"node_load5{instance=\\\"$node\\\"}\",\"legend\":\"load5\"},{\"expr\":\"node_load15{instance=\\\"$node\\\"}\",\"legend\":\"load15\"}],\"name\":\"CPU负载\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{\"steps\":[]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "内存详情", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_HugePages_Total{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Total\"},{\"expr\":\"node_memory_Hugepagesize_bytes{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Size\"},{\"expr\":\"node_memory_HugePages_Surp{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Surp \"},{\"expr\":\"node_memory_HugePages_Free{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Free\"},{\"expr\":\"node_memory_HugePages_Rsvd{instance=\\\"$node\\\"}\",\"legend\":\"HugePages_Rsvd\"},{\"expr\":\"node_memory_AnonHugePages_bytes{instance=\\\"$node\\\"}\",\"legend\":\"AnonHugePages\"},{\"expr\":\"node_memory_Inactive_file_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Inactive_file\"},{\"expr\":\"node_memory_Inactive_anon_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Inactive_anon\"},{\"expr\":\"node_memory_Active_file_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Active_file\"},{\"expr\":\"node_memory_Active_anon_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Active_anon\"},{\"expr\":\"node_memory_Unevictable_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Unevictable\"},{\"expr\":\"node_memory_AnonPages_bytes{instance=\\\"$node\\\"}\",\"legend\":\"AnonPages\"},{\"expr\":\"node_memory_Shmem_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Shmem\"},{\"expr\":\"node_memory_Mapped_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Mapped\"},{\"expr\":\"node_memory_Cached_bytes{instance=\\\"$node\\\"} \",\"legend\":\"Cache\"},{\"expr\":\"node_memory_SwapCached_bytes{instance=\\\"$node\\\"}\",\"legend\":\"SwapCache\"},{\"expr\":\"node_memory_Mlocked_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Mlocked\"},{\"expr\":\"node_memory_Buffers_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Buffers\"}],\"name\":\"用户态内存使用\",\"description\":\"内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) \",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.35,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_Slab_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Slab \"},{\"expr\":\"node_memory_SReclaimable_bytes{instance=\\\"$node\\\"}\",\"legend\":\"SReclaimable \"},{\"expr\":\"node_memory_SUnreclaim_bytes{instance=\\\"$node\\\"}\",\"legend\":\"SUnreclaim \"},{\"expr\":\"node_memory_VmallocUsed_bytes{instance=\\\"$node\\\"}\",\"legend\":\"VmallocUsed\"},{\"expr\":\"node_memory_VmallocChunk_bytes{instance=\\\"$node\\\"}\",\"legend\":\"VmallocChunk\"},{\"expr\":\"node_memory_KernelStack_bytes{instance=\\\"$node\\\"}\",\"legend\":\"KernelStack\"},{\"expr\":\"node_memory_Bounce_bytes{instance=\\\"$node\\\"}\",\"legend\":\"Bounce \"}],\"name\":\"内核态内存使用\",\"description\":\"内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) \",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_DirectMap1G_bytes{instance=\\\"$node\\\"}\",\"legend\":\"DirectMap1G\"},{\"expr\":\"node_memory_DirectMap2M_bytes{instance=\\\"$node\\\"}\",\"legend\":\"DirectMap2M\"},{\"expr\":\"node_memory_DirectMap4k_bytes{instance=\\\"$node\\\"}\",\"legend\":\"DirectMap4K\"}],\"name\":\"TLB效率\",\"description\":\"/proc/meminfo中的DirectMap所统计的不是关于内存的使用,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的, 比如2MB,4MB,甚至1GB的内存页,视不同的硬件而定。”DirectMap4k”表示映射为4kB的内存数量, “DirectMap2M”表示映射为2MB的内存数量,以此类推。所以DirectMap其实是一个反映TLB效率的指标\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_memory_NFS_Unstable_bytes{instance=\\\"$node\\\"}\",\"legend\":\"NFS Unstable\"},{\"expr\":\"node_memory_Writeback_bytes{instance=\\\"$node\\\"}\",\"legend\":\"memory_Writeback\"},{\"expr\":\"node_memory_Dirty_bytes{instance=\\\"$node\\\"}\",\"legend\":\"memory_Dirty\"}],\"name\":\"dirty page\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "磁盘详情", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"node_filesystem_avail_bytes{instance=\\\"$node\\\",device!~'rootfs', device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Available\"},{\"expr\":\"node_filesystem_free_bytes{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Free\"},{\"expr\":\"node_filesystem_size_bytes{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Total\"}],\"name\":\"磁盘空间\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":null},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filesystem_files{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - total\"},{\"expr\":\"node_filesystem_files_free{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - free\"}],\"name\":\"inode\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filefd_maximum{instance=\\\"$node\\\"}\",\"legend\":\"Max open files\"},{\"expr\":\"node_filefd_allocated{instance=\\\"$node\\\"}\",\"legend\":\"Open files\"}],\"name\":\"fd使用\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_filesystem_readonly{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - ReadOnly\"},{\"expr\":\"node_filesystem_device_error{instance=\\\"$node\\\",device!~'rootfs',device!~\\\"tmpfs\\\",mountpoint!~\\\"/var/lib.*\\\"}\",\"legend\":\"{{mountpoint}} - Device error\"}],\"name\":\"读写错误\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_reads_completed_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}}-reads\"},{\"expr\":\"rate(node_disk_writes_completed_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Writes\"},{\"expr\":\"rate(node_disk_reads_merged_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Read merged\"},{\"expr\":\"rate(node_disk_writes_merged_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Write merged\"}],\"name\":\"IO/Merged次数\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_read_bytes_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}}-Read bytes\"},{\"expr\":\"rate(node_disk_written_bytes_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}} - Written bytes\"}],\"name\":\"读写数据大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_io_time_seconds_total{instance=\\\"$node\\\"}[1m])\",\"legend\":\"{{device}}\"}],\"name\":\"io util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(rate(node_disk_read_time_seconds_total{instance=\\\"$node\\\"}[1m]) / rate(node_disk_reads_completed_total{instance=\\\"$node\\\"}[1m])+rate(node_disk_write_time_seconds_total{instance=\\\"$node\\\"}[1m]) / rate(node_disk_writes_completed_total{instance=\\\"$node\\\"}[1m]))*1000\",\"legend\":\"{{device}}\"}],\"name\":\"io await\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.64,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(rate(node_disk_read_bytes_total{instance=\\\"$node\\\"}[1m]) + rate(node_disk_written_bytes_total{instance=\\\"$node\\\"}[1m]))\\n/\\n(rate(node_disk_reads_completed_total{instance=\\\"$node\\\"}[1m]) + rate(node_disk_writes_completed_total{instance=\\\"$node\\\"}[1m]))\",\"legend\":\"avgrq-sz\"}],\"name\":\"avgrq-sz\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":4,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_disk_io_time_weighted_seconds_total{instance=\\\"$node\\\"}[1m])\\n\",\"legend\":\"{{device}}\"}],\"name\":\"avgqu-sz\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":4,\"i\":\"9\"}}", - "weight": 0 - } - ] - }, - { - "name": "网络详情", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_bytes_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])*8\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_bytes_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])*8\",\"legend\":\"{{device}} - out\"}],\"name\":\"出入流量大小\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_packets_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_packets_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - out\"}],\"name\":\"packets\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_errs_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_errs_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - out\"}],\"name\":\"error\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(node_network_receive_drop_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - in\"},{\"expr\":\"rate(node_network_transmit_drop_total{instance=\\\"$node\\\",device=~\\\"e.*\\\"}[1m])\",\"legend\":\"{{device}} - out\"}],\"name\":\"drop\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_nf_conntrack_entries{instance=\\\"$node\\\"}\",\"legend\":\"NF conntrack entries\"},{\"expr\":\"node_nf_conntrack_entries_limit{instance=\\\"$node\\\"}\",\"legend\":\"NF conntrack limit\"}],\"name\":\"nf_conntrack\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_sockstat_TCP_alloc{instance=\\\"$node\\\"}\",\"legend\":\"TCP_alloc\"},{\"expr\":\"node_sockstat_TCP_inuse{instance=\\\"$node\\\"}\",\"legend\":\"TCP_inuse\"},{\"expr\":\"node_sockstat_TCP_orphan{instance=\\\"$node\\\"}\",\"legend\":\"TCP_orphan\"},{\"expr\":\"node_sockstat_TCP_tw{instance=\\\"$node\\\"}\",\"legend\":\"TCP_tw\"},{\"expr\":\"node_netstat_Tcp_CurrEstab{instance=\\\"$node\\\"}\",\"legend\":\"TCP_CurrEstab\"}],\"name\":\"tcp\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.27,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"node_sockstat_sockets_used{instance=\\\"$node\\\"}\",\"legend\":\"Sockets_used\"}],\"name\":\"socket\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"6\"}}", - "weight": 0 - } - ] - } - ] - } -] \ No newline at end of file diff --git a/etc/dashboards/linux_by_telegraf.json b/etc/dashboards/linux_by_telegraf.json deleted file mode 100644 index 990a68f8fd37d408cde3058189e7e3761bfb96b5..0000000000000000000000000000000000000000 --- a/etc/dashboards/linux_by_telegraf.json +++ /dev/null @@ -1,1523 +0,0 @@ -{ - "name": "HOST - Telegraf 模板", - "tags": "", - "configs": { - "var": [ - { - "name": "ident", - "definition": "label_values(system_load1,ident)" - } - ], - "links": [ - { - "title": "n9e", - "url": "https://n9e.gitee.io/", - "targetBlank": true - }, - { - "title": "author", - "url": "http://flashcat.cloud/", - "targetBlank": true - } - ], - "version": "2.0.0", - "panels": [ - { - "id": "0f6a1394-7cf9-4958-bcfe-2fbb59e77c12", - "type": "row", - "name": "整体概况", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 0, - "i": "0f6a1394-7cf9-4958-bcfe-2fbb59e77c12" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "count(system_load1)" - } - ], - "name": "监控机器数", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 50 - } - }, - "options": { - "standardOptions": {} - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 0, - "y": 1, - "i": "877b6db5-e82c-499a-9ebc-8ad72c2891a8" - }, - "id": "877b6db5-e82c-499a-9ebc-8ad72c2891a8" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, mem_used_percent)" - } - ], - "name": "内存率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 9, - "x": 3, - "y": 1, - "i": "29a3e6ae-d278-49b3-972b-f12a6c7c091c" - }, - "id": "29a3e6ae-d278-49b3-972b-f12a6c7c091c" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (100-cpu_usage_idle{cpu=\"cpu-total\"}))" - } - ], - "name": "cpu使用率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 12, - "y": 1, - "i": "9f2a24d5-d19f-4651-b76d-add6b9011821" - }, - "id": "9f2a24d5-d19f-4651-b76d-add6b9011821" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (disk_used_percent{path!~\"/var.*\"}))", - "legend": "{{ident}}-{{path}}" - } - ], - "name": "磁盘分区使用率 top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 0, - "y": 2, - "i": "dcd60296-db84-4562-99f3-2829c2f064a4" - }, - "id": "dcd60296-db84-4562-99f3-2829c2f064a4" - }, - { - "targets": [ - { - "refId": "A", - "expr": "topk(10, (rate(diskio_io_time[1m])/10))", - "legend": "" - } - ], - "name": "设备io util top10", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 12, - "x": 12, - "y": 2, - "i": "ef7df29d-7dce-4788-ae42-d21d842c67d6" - }, - "id": "ef7df29d-7dce-4788-ae42-d21d842c67d6" - }, - { - "id": "7b2c5cb2-fe3b-4596-95a1-37da06cd6498", - "type": "row", - "name": "单机概况", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 5, - "i": "7b2c5cb2-fe3b-4596-95a1-37da06cd6498" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "system_uptime{ident=\"$ident\"}" - } - ], - "name": "启动时长", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [], - "standardOptions": { - "util": "humantimeSeconds", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 0, - "y": 6, - "i": "50f09231-fc5e-4f6d-9367-a3158504689b" - }, - "id": "50f09231-fc5e-4f6d-9367-a3158504689b" - }, - { - "targets": [ - { - "refId": "A", - "expr": "100-cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}" - } - ], - "name": "CPU使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 6, - "y": 6, - "i": "d44e951d-c333-4ed9-9303-9c8d29da7993" - }, - "id": "d44e951d-c333-4ed9-9303-9c8d29da7993" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_used_percent{ident=\"$ident\"}" - } - ], - "name": "内存使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 30 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 12, - "y": 6, - "i": "278c2fa1-0b19-4718-8b12-fb1c2e776258" - }, - "id": "278c2fa1-0b19-4718-8b12-fb1c2e776258" - }, - { - "targets": [ - { - "refId": "A", - "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}/linux_sysctl_fs_file_max{ident=\"$ident\"}*100" - } - ], - "name": "FD使用率", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 25 - } - }, - "options": { - "valueMappings": [ - { - "type": "range", - "match": { - "from": 0, - "to": 50 - }, - "result": { - "color": "#129b22" - } - }, - { - "type": "range", - "match": { - "from": 50, - "to": 100 - }, - "result": { - "color": "#f51919" - } - } - ], - "standardOptions": { - "util": "percent", - "decimals": 2 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 18, - "y": 6, - "i": "484afcd4-7b25-4af1-8e95-88cc675f7f43" - }, - "id": "484afcd4-7b25-4af1-8e95-88cc675f7f43" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_swap_total{ident=\"$ident\"}-mem_swap_free{ident=\"$ident\"}" - } - ], - "name": "SWAP使用", - "custom": { - "textMode": "value", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": { - "value": 40 - } - }, - "options": { - "valueMappings": [], - "standardOptions": { - "util": "bytesIEC", - "decimals": 1 - } - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 3, - "x": 21, - "y": 6, - "i": "142f63d7-4979-4354-81b5-a9c5ec81fae9" - }, - "id": "142f63d7-4979-4354-81b5-a9c5ec81fae9" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_used_percent{ident=\"$ident\"}", - "legend": "{{path}}" - } - ], - "name": "磁盘使用率", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 0, - "y": 7, - "i": "4d6ec15c-8fdd-47db-a9f7-a57f03009e66" - }, - "id": "4d6ec15c-8fdd-47db-a9f7-a57f03009e66" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_inodes_used{ident=\"$ident\"}/disk_inodes_total{ident=\"$ident\"}", - "legend": "{{path}}" - } - ], - "name": "inode使用率", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 8, - "y": 7, - "i": "2991ad6b-c219-4f1d-b298-e195cf35cfec" - }, - "id": "2991ad6b-c219-4f1d-b298-e195cf35cfec" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_io_time{ident=\"$ident\"}[1m])/10", - "legend": "{{name}}" - } - ], - "name": "io_util", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "percent", - "decimals": 1 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 3, - "w": 8, - "x": 16, - "y": 7, - "i": "935435db-5a1e-4330-b95f-825e91e9d99e" - }, - "id": "935435db-5a1e-4330-b95f-825e91e9d99e" - }, - { - "id": "1a19ca3f-3296-43ef-a36c-523ead023489", - "type": "row", - "name": "系统指标", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 10, - "i": "1a19ca3f-3296-43ef-a36c-523ead023489" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "processes_total{ident=\"$ident\"}" - } - ], - "name": "进程总数", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 2000, - "color": "#fa2a05" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 11, - "i": "bab39b5e-63cf-4e88-a474-4ea8f2585d8e" - }, - "id": "bab39b5e-63cf-4e88-a474-4ea8f2585d8e" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(kernel_context_switches{ident=\"$ident\"}[1m])", - "legend": "context_switches" - }, - { - "expr": "rate(kernel_interrupts{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "kernel_interrupts" - } - ], - "name": "上下文切换/中断", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 11, - "i": "0ea75485-cc11-4b44-b13f-911429d9e103" - }, - "id": "0ea75485-cc11-4b44-b13f-911429d9e103" - }, - { - "targets": [ - { - "refId": "A", - "expr": "kernel_entropy_avail{ident=\"$ident\"}", - "legend": "entropy_avail" - } - ], - "name": "熵池大小", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 100, - "color": "#f50505" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 11, - "i": "32d764fa-ed86-4099-b0f8-1cb8c7f67315" - }, - "id": "32d764fa-ed86-4099-b0f8-1cb8c7f67315" - }, - { - "id": "fe779989-795e-4ef6-9280-fdea929bb397", - "type": "row", - "name": "CPU", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 18, - "i": "fe779989-795e-4ef6-9280-fdea929bb397" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}", - "legend": "cpu_usage_idle" - } - ], - "name": "CPU空闲率", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": { - "steps": [ - { - "value": 10, - "color": "#f20202" - } - ] - } - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 19, - "i": "f5b86c4f-2104-41a4-9d01-a62ee64c04ff" - }, - "id": "f5b86c4f-2104-41a4-9d01-a62ee64c04ff" - }, - { - "targets": [ - { - "refId": "A", - "expr": "cpu_usage_guest{ident=\"$ident\",cpu=\"cpu-total\"}", - "legend": "" - }, - { - "expr": "cpu_usage_iowait{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "B", - "legend": "" - }, - { - "expr": "cpu_usage_user{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "C" - }, - { - "expr": "cpu_usage_system{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "D" - }, - { - "expr": "cpu_usage_irq{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "E" - }, - { - "expr": "cpu_usage_softirq{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "F" - }, - { - "expr": "cpu_usage_nice{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "G" - }, - { - "expr": "cpu_usage_steal{ident=\"$ident\",cpu=\"cpu-total\"}", - "refId": "H" - } - ], - "name": "CPU使用率详情", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 19, - "i": "e833715f-065c-4b1b-9f0d-e1223b6992b8" - }, - "id": "e833715f-065c-4b1b-9f0d-e1223b6992b8" - }, - { - "targets": [ - { - "refId": "A", - "expr": "system_load15{ident=\"$ident\"}" - }, - { - "expr": "system_load1{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "system_load5{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "CPU负载", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 19, - "i": "b19f9420-9ce2-4d3c-86bf-fc247c8b760e" - }, - "id": "b19f9420-9ce2-4d3c-86bf-fc247c8b760e" - }, - { - "id": "e9ebdac6-4a87-4a79-b125-e5a258a968d0", - "type": "row", - "name": "内存详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 26, - "i": "e9ebdac6-4a87-4a79-b125-e5a258a968d0" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_active{ident=\"$ident\"}" - }, - { - "expr": "mem_cached{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "mem_buffered{ident=\"$ident\"}", - "refId": "C" - }, - { - "expr": "mem_inactive{ident=\"$ident\"}", - "refId": "D" - }, - { - "expr": "mem_mapped{ident=\"$ident\"}", - "refId": "E" - }, - { - "expr": "mem_shared{ident=\"$ident\"}", - "refId": "F" - }, - { - "expr": "mem_swap_cached{ident=\"$ident\"}", - "refId": "G" - } - ], - "name": "用户态内存使用", - "description": "内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) ", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 12, - "x": 0, - "y": 27, - "i": "655608d2-0d6d-46ed-9e6a-c482edaeacec" - }, - "id": "655608d2-0d6d-46ed-9e6a-c482edaeacec" - }, - { - "targets": [ - { - "refId": "A", - "expr": "mem_slab{ident=\"$ident\"}" - }, - { - "expr": "mem_sreclaimable{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "mem_sunreclaim{ident=\"$ident\"}", - "refId": "C" - }, - { - "expr": "mem_vmalloc_used{ident=\"$ident\"}", - "refId": "D" - }, - { - "expr": "mem_vmalloc_chunk{ident=\"$ident\"}", - "refId": "E" - } - ], - "name": "内核态内存使用", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 12, - "x": 12, - "y": 27, - "i": "d57fe702-e21f-45ee-9b36-31695e698059" - }, - "id": "d57fe702-e21f-45ee-9b36-31695e698059" - }, - { - "id": "893315c8-54ac-4eaf-9072-d0a2debd3404", - "type": "row", - "name": "磁盘详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 34, - "i": "893315c8-54ac-4eaf-9072-d0a2debd3404" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_free{ident=\"$ident\"}" - }, - { - "expr": "disk_total{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "disk_used{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "磁盘空间", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": null - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 35, - "i": "b39143a6-9ca8-4732-9100-0a8c029440b5" - }, - "id": "b39143a6-9ca8-4732-9100-0a8c029440b5" - }, - { - "targets": [ - { - "refId": "A", - "expr": "linux_sysctl_fs_file_max{ident=\"$ident\"}" - }, - { - "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}", - "refId": "B" - } - ], - "name": "fd使用", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 35, - "i": "3a13d95d-a311-4b0b-87f2-4216cac9a533" - }, - "id": "3a13d95d-a311-4b0b-87f2-4216cac9a533" - }, - { - "targets": [ - { - "refId": "A", - "expr": "disk_inodes_total{ident=\"$ident\",path!~\"/var.*\"}", - "legend": "{{path}}-total" - }, - { - "expr": "disk_inodes_used{ident=\"$ident\",path!~\"/var.*\"}", - "refId": "B", - "legend": "{{path}}-used" - } - ], - "name": "inode", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 35, - "i": "6a1fa455-a385-456a-a32b-30119082f453" - }, - "id": "6a1fa455-a385-456a-a32b-30119082f453" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_reads{ident=\"$ident\"}[1m])", - "legend": "{{name}}-read" - }, - { - "expr": "rate(diskio_writes{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{name}}-writes" - } - ], - "name": "IOPS", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 0, - "y": 37, - "i": "c74e5155-0e3c-4cb4-8e57-ce8af46ddf90" - }, - "id": "c74e5155-0e3c-4cb4-8e57-ce8af46ddf90" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_read_bytes{ident=\"$ident\"}[1m])", - "legend": "{{name}}-read" - }, - { - "expr": "rate(diskio_write_bytes{ident=\"$ident\"}[1m])", - "refId": "B", - "legend": "{{name}}-writes" - } - ], - "name": "IO吞吐量", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 8, - "y": 37, - "i": "c522e6f5-fb3d-4fc0-9ae6-7ec002e55e95" - }, - "id": "c522e6f5-fb3d-4fc0-9ae6-7ec002e55e95" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(diskio_write_time{ident=\"$ident\"}[1m])/rate(diskio_writes{ident=\"$ident\"}[1m])+rate(diskio_read_time{ident=\"$ident\"}[1m])/rate(diskio_reads{ident=\"$ident\"}[1m])", - "legend": "{{name}}" - } - ], - "name": "iowait", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 8, - "x": 16, - "y": 37, - "i": "47fd7f57-ed4a-43cf-86e3-628c2f697769" - }, - "id": "47fd7f57-ed4a-43cf-86e3-628c2f697769" - }, - { - "id": "f8c5e284-5e23-4646-976c-23511f4f908d", - "type": "row", - "name": "网络详情", - "layout": { - "h": 1, - "w": 24, - "x": 0, - "y": 44, - "i": "f8c5e284-5e23-4646-976c-23511f4f908d" - }, - "collapsed": true - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_bytes_recv{ident=\"$ident\",interface=~\"eth.*\"}[1m])*8", - "legend": "{{interface}}-recv" - }, - { - "expr": "rate(net_bytes_sent{ident=\"$ident\",interface=~\"eth.*\"}[1m])*8", - "refId": "B", - "legend": "{{interface}}-sent" - } - ], - "name": "网络流量", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "util": "bytesIEC", - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 0, - "y": 45, - "i": "7fe8774f-7d03-4514-a6b6-b626d2a95265" - }, - "id": "7fe8774f-7d03-4514-a6b6-b626d2a95265" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_packets_recv{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "legend": "{{interface}}-recv" - }, - { - "expr": "rate(net_packets_sent{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "refId": "B", - "legend": "{{interface}}-sent" - } - ], - "name": "packets", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 6, - "y": 45, - "i": "d030e5e7-06cd-42e9-b1e5-0c32b51f853e" - }, - "id": "d030e5e7-06cd-42e9-b1e5-0c32b51f853e" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_err_in{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "legend": "{{interface}}-in" - }, - { - "expr": "rate(net_err_out{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "refId": "B", - "legend": "{{interface}}-out" - } - ], - "name": "error", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 12, - "y": 45, - "i": "330f5fd9-aca5-4619-b81b-33203256d560" - }, - "id": "330f5fd9-aca5-4619-b81b-33203256d560" - }, - { - "targets": [ - { - "refId": "A", - "expr": "rate(net_drop_in{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "legend": "{{interface}}-in" - }, - { - "expr": "rate(net_drop_out{ident=\"$ident\",interface=~\"eth.*\"}[1m])", - "refId": "B", - "legend": "{{interface}}-out" - } - ], - "name": "drop", - "options": { - "tooltip": { - "mode": "all", - "sort": "desc" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": { - "decimals": 0 - }, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 6, - "x": 18, - "y": 45, - "i": "34a73b20-56d7-4edb-b6f7-acc93d00a026" - }, - "id": "34a73b20-56d7-4edb-b6f7-acc93d00a026" - }, - { - "targets": [ - { - "refId": "A", - "expr": "netstat_tcp_established{ident=\"$ident\"}" - }, - { - "expr": "netstat_tcp_listen{ident=\"$ident\"}", - "refId": "B" - }, - { - "expr": "netstat_tcp_time_wait{ident=\"$ident\"}", - "refId": "C" - } - ], - "name": "tcp", - "options": { - "tooltip": { - "mode": "all", - "sort": "none" - }, - "legend": { - "displayMode": "hidden" - }, - "standardOptions": {}, - "thresholds": {} - }, - "custom": { - "drawStyle": "lines", - "lineInterpolation": "smooth", - "fillOpacity": 0.5, - "stack": "off" - }, - "version": "2.0.0", - "type": "timeseries", - "layout": { - "h": 7, - "w": 24, - "x": 0, - "y": 47, - "i": "7664d34f-7bcf-4431-a6a7-4d924d2e176d" - }, - "id": "7664d34f-7bcf-4431-a6a7-4d924d2e176d" - } - ] - } -} \ No newline at end of file diff --git a/etc/dashboards/mysql_by_categraf.json b/etc/dashboards/mysql_by_categraf.json deleted file mode 100644 index 0c31c14782b34a78d0c3b3f85ef780b5a06b5bc0..0000000000000000000000000000000000000000 --- a/etc/dashboards/mysql_by_categraf.json +++ /dev/null @@ -1,119 +0,0 @@ -[ - { - "name": "MySQL Overview - 模板", - "tags": "Prometheus MySQL", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(mysql_global_status_uptime, instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(mysql_global_status_uptime{instance=~\\\"$instance\\\"})\"}],\"name\":\"MySQL Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#ec7718\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#369603\"}}],\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_queries{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Current QPS\",\"description\":\"mysql_global_status_queries\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":100},\"result\":{\"color\":\"#05a31f\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#ea3939\"}}],\"standardOptions\":{\"decimals\":2}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"avg(mysql_global_variables_innodb_buffer_pool_size{instance=~\\\"$instance\\\"})\"}],\"name\":\"InnoDB Buffer Pool\",\"description\":\"**InnoDB Buffer Pool Size**\\n\\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(increase(mysql_global_status_table_locks_waited{instance=~\\\"$instance\\\"}[5m]))\"}],\"name\":\"Table Locks Waited(5min)\",\"description\":\"**Table Locks**\\n\\nMySQL takes a number of different locks for varying reasons. In this graph we see how many Table level locks MySQL has requested from the storage engine. In the case of InnoDB, many times the locks could actually be row locks as it only takes table level locks in a few specific cases.\\n\\nIt is most useful to compare Locks Immediate and Locks Waited. If Locks waited is rising, it means you have lock contention. Otherwise, Locks Immediate rising and falling is normal activity.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":1},\"result\":{\"color\":\"#e70d0d\"}},{\"type\":\"range\",\"match\":{\"to\":1},\"result\":{\"color\":\"#53b503\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Connections", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Connections\"},{\"expr\":\"sum(mysql_global_status_max_used_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Used Connections\"},{\"expr\":\"sum(mysql_global_variables_max_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Connections\"},{\"expr\":\"sum(rate(mysql_global_status_aborted_connects{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Aborted Connections\"}],\"name\":\"MySQL Connections\",\"description\":\"**Max Connections** \\n\\nMax Connections is the maximum permitted number of simultaneous client connections. By default, this is 151. Increasing this value increases the number of file descriptors that mysqld requires. If the required number of descriptors are not available, the server reduces the value of Max Connections.\\n\\nmysqld actually permits Max Connections + 1 clients to connect. The extra connection is reserved for use by accounts that have the SUPER privilege, such as root.\\n\\nMax Used Connections is the maximum number of connections that have been in use simultaneously since the server started.\\n\\nConnections is the number of connection attempts (successful or not) to the MySQL server.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Connected\"},{\"expr\":\"sum(mysql_global_status_threads_running{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Running\"}],\"name\":\"MySQL Client Thread Activity\",\"description\":\"Threads Connected is the number of open connections, while Threads Running is the number of threads not sleeping.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Query Performance", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_created_tmp_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_disk_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Disk Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_files{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Files\"}],\"name\":\"MySQL Temporary Objects\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.64,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_select_full_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_full_range_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Range Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_range{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range\"},{\"expr\":\"sum(rate(mysql_global_status_select_range_check{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range Check\"},{\"expr\":\"sum(rate(mysql_global_status_select_scan{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Scan\"}],\"name\":\"MySQL Select Types\",\"description\":\"**MySQL Select Types**\\n\\nAs with most relational databases, selecting based on indexes is more efficient than scanning an entire table's data. Here we see the counters for selects not done with indexes.\\n\\n* ***Select Scan*** is how many queries caused full table scans, in which all the data in the table had to be read and either discarded or returned.\\n* ***Select Range*** is how many queries used a range scan, which means MySQL scanned all rows in a given range.\\n* ***Select Full Join*** is the number of joins that are not joined on an index, this is usually a huge performance hit.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"stack\":\"off\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.41},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_sort_rows{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Rows\"},{\"expr\":\"sum(rate(mysql_global_status_sort_range{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Range\"},{\"expr\":\"sum(rate(mysql_global_status_sort_merge_passes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Merge Passes\"},{\"expr\":\"sum(rate(mysql_global_status_sort_scan{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Scan\"}],\"name\":\"MySQL Sorts\",\"description\":\"**MySQL Sorts**\\n\\nDue to a query's structure, order, or other requirements, MySQL sorts the rows before returning them. For example, if a table is ordered 1 to 10 but you want the results reversed, MySQL then has to sort the rows to return 10 to 1.\\n\\nThis graph also shows when sorts had to scan a whole table or a given range of a table in order to return the results and which could not have been sorted via an index.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_slow_queries{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Slow Queries\"}],\"name\":\"MySQL Slow Queries\",\"description\":\"**MySQL Slow Queries**\\n\\nSlow queries are defined as queries being slower than the long_query_time setting. For example, if you have long_query_time set to 3, all queries that take longer than 3 seconds to complete will show on this graph.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"bars\",\"stack\":\"off\",\"fillOpacity\":0.81},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_bytes_received{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Inbound\"},{\"expr\":\"sum(rate(mysql_global_status_bytes_sent{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Outbound\"}],\"name\":\"MySQL Network Traffic\",\"description\":\"**MySQL Network Traffic**\\n\\nHere we can see how much network traffic is generated by MySQL. Outbound is network traffic sent from MySQL and Inbound is network traffic MySQL has received.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands, Handlers", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"topk(10, rate(mysql_global_status_commands_total{instance=~\\\"$instance\\\"}[5m])>0)\",\"legend\":\"Com_{{command}}\"}],\"name\":\"Top Command Counters\",\"description\":\"**Top Command Counters**\\n\\nThe Com_{{xxx}} statement counter variables indicate the number of times each xxx statement has been executed. There is one status variable for each type of statement. For example, Com_delete and Com_update count [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements, respectively. Com_delete_multi and Com_update_multi are similar but apply to [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements that use multiple-table syntax.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.2,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler!~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Handlers\",\"description\":\"**MySQL Handlers**\\n\\nHandler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes.\\n\\nThis is in fact the layer between the Storage Engine and MySQL.\\n\\n* `read_rnd_next` is incremented when the server performs a full table scan and this is a counter you don't really want to see with a high value.\\n* `read_key` is incremented when a read is done with an index.\\n* `read_next` is incremented when the storage engine is asked to 'read the next index entry'. A high value means a lot of index scans are being done.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":3},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler=~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Transaction Handlers\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Open Files", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_variables_open_files_limit{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Files Limit\"},{\"expr\":\"mysql_global_status_open_files{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Files\"}],\"name\":\"MySQL Open Files\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Table Openings", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n/\\n(\\nrate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n+\\nrate(mysql_global_status_table_open_cache_misses{instance=~\\\"$instance\\\"}[5m])\\n)\",\"legend\":\"Table Open Cache Hit Ratio\"}],\"name\":\"Table Open Cache Hit Ratio Mysql 5.6.6+\",\"description\":\"**MySQL Table Open Cache Status**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_status_open_tables{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Tables\"},{\"expr\":\"mysql_global_variables_table_open_cache{instance=~\\\"$instance\\\"}\",\"legend\":\"Table Open Cache\"}],\"name\":\"MySQL Open Tables\",\"description\":\"**MySQL Open Tables**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/mysql_by_exporter.json b/etc/dashboards/mysql_by_exporter.json deleted file mode 100644 index b76ef591b2c5275203e5afdd12f6316fc795cf4c..0000000000000000000000000000000000000000 --- a/etc/dashboards/mysql_by_exporter.json +++ /dev/null @@ -1,119 +0,0 @@ -[ - { - "name": "MySQL Overview - 模板", - "tags": "Prometheus MySQL", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(mysql_global_status_uptime, instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(mysql_global_status_uptime{instance=~\\\"$instance\\\"})\"}],\"name\":\"MySQL Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#ec7718\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#369603\"}}],\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_queries{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Current QPS\",\"description\":\"mysql_global_status_queries\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":100},\"result\":{\"color\":\"#05a31f\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#ea3939\"}}],\"standardOptions\":{\"decimals\":2}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"avg(mysql_global_variables_innodb_buffer_pool_size{instance=~\\\"$instance\\\"})\"}],\"name\":\"InnoDB Buffer Pool\",\"description\":\"**InnoDB Buffer Pool Size**\\n\\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(increase(mysql_global_status_table_locks_waited{instance=~\\\"$instance\\\"}[5m]))\"}],\"name\":\"Table Locks Waited(5min)\",\"description\":\"**Table Locks**\\n\\nMySQL takes a number of different locks for varying reasons. In this graph we see how many Table level locks MySQL has requested from the storage engine. In the case of InnoDB, many times the locks could actually be row locks as it only takes table level locks in a few specific cases.\\n\\nIt is most useful to compare Locks Immediate and Locks Waited. If Locks waited is rising, it means you have lock contention. Otherwise, Locks Immediate rising and falling is normal activity.\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":1},\"result\":{\"color\":\"#e70d0d\"}},{\"type\":\"range\",\"match\":{\"to\":1},\"result\":{\"color\":\"#53b503\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Connections", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Connections\"},{\"expr\":\"sum(mysql_global_status_max_used_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Used Connections\"},{\"expr\":\"sum(mysql_global_variables_max_connections{instance=~\\\"$instance\\\"})\",\"legend\":\"Max Connections\"},{\"expr\":\"sum(rate(mysql_global_status_aborted_connects{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Aborted Connections\"}],\"name\":\"MySQL Connections\",\"description\":\"**Max Connections** \\n\\nMax Connections is the maximum permitted number of simultaneous client connections. By default, this is 151. Increasing this value increases the number of file descriptors that mysqld requires. If the required number of descriptors are not available, the server reduces the value of Max Connections.\\n\\nmysqld actually permits Max Connections + 1 clients to connect. The extra connection is reserved for use by accounts that have the SUPER privilege, such as root.\\n\\nMax Used Connections is the maximum number of connections that have been in use simultaneously since the server started.\\n\\nConnections is the number of connection attempts (successful or not) to the MySQL server.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(mysql_global_status_threads_connected{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Connected\"},{\"expr\":\"sum(mysql_global_status_threads_running{instance=~\\\"$instance\\\"})\",\"legend\":\"Threads Running\"}],\"name\":\"MySQL Client Thread Activity\",\"description\":\"Threads Connected is the number of open connections, while Threads Running is the number of threads not sleeping.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Query Performance", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_created_tmp_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_disk_tables{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Disk Tables\"},{\"expr\":\"sum(rate(mysql_global_status_created_tmp_files{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Created Tmp Files\"}],\"name\":\"MySQL Temporary Objects\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.64,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_select_full_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_full_range_join{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Full Range Join\"},{\"expr\":\"sum(rate(mysql_global_status_select_range{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range\"},{\"expr\":\"sum(rate(mysql_global_status_select_range_check{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Range Check\"},{\"expr\":\"sum(rate(mysql_global_status_select_scan{ instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Select Scan\"}],\"name\":\"MySQL Select Types\",\"description\":\"**MySQL Select Types**\\n\\nAs with most relational databases, selecting based on indexes is more efficient than scanning an entire table's data. Here we see the counters for selects not done with indexes.\\n\\n* ***Select Scan*** is how many queries caused full table scans, in which all the data in the table had to be read and either discarded or returned.\\n* ***Select Range*** is how many queries used a range scan, which means MySQL scanned all rows in a given range.\\n* ***Select Full Join*** is the number of joins that are not joined on an index, this is usually a huge performance hit.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"stack\":\"off\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.41},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_sort_rows{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Rows\"},{\"expr\":\"sum(rate(mysql_global_status_sort_range{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Range\"},{\"expr\":\"sum(rate(mysql_global_status_sort_merge_passes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Merge Passes\"},{\"expr\":\"sum(rate(mysql_global_status_sort_scan{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Sort Scan\"}],\"name\":\"MySQL Sorts\",\"description\":\"**MySQL Sorts**\\n\\nDue to a query's structure, order, or other requirements, MySQL sorts the rows before returning them. For example, if a table is ordered 1 to 10 but you want the results reversed, MySQL then has to sort the rows to return 10 to 1.\\n\\nThis graph also shows when sorts had to scan a whole table or a given range of a table in order to return the results and which could not have been sorted via an index.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_slow_queries{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Slow Queries\"}],\"name\":\"MySQL Slow Queries\",\"description\":\"**MySQL Slow Queries**\\n\\nSlow queries are defined as queries being slower than the long_query_time setting. For example, if you have long_query_time set to 3, all queries that take longer than 3 seconds to complete will show on this graph.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"bars\",\"stack\":\"off\",\"fillOpacity\":0.81},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(mysql_global_status_bytes_received{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Inbound\"},{\"expr\":\"sum(rate(mysql_global_status_bytes_sent{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"Outbound\"}],\"name\":\"MySQL Network Traffic\",\"description\":\"**MySQL Network Traffic**\\n\\nHere we can see how much network traffic is generated by MySQL. Outbound is network traffic sent from MySQL and Inbound is network traffic MySQL has received.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands, Handlers", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"topk(10, rate(mysql_global_status_commands_total{instance=~\\\"$instance\\\"}[5m])>0)\",\"legend\":\"Com_{{command}}\"}],\"name\":\"Top Command Counters\",\"description\":\"**Top Command Counters**\\n\\nThe Com_{{xxx}} statement counter variables indicate the number of times each xxx statement has been executed. There is one status variable for each type of statement. For example, Com_delete and Com_update count [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements, respectively. Com_delete_multi and Com_update_multi are similar but apply to [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements that use multiple-table syntax.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.2,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler!~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Handlers\",\"description\":\"**MySQL Handlers**\\n\\nHandler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes.\\n\\nThis is in fact the layer between the Storage Engine and MySQL.\\n\\n* `read_rnd_next` is incremented when the server performs a full table scan and this is a counter you don't really want to see with a high value.\\n* `read_key` is incremented when a read is done with an index.\\n* `read_next` is incremented when the storage engine is asked to 'read the next index entry'. A high value means a lot of index scans are being done.\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":3},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_handlers_total{instance=~\\\"$instance\\\", handler=~\\\"commit|rollback|savepoint.*|prepare\\\"}[5m])\",\"legend\":\"{{handler}}\"}],\"name\":\"MySQL Transaction Handlers\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Open Files", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_variables_open_files_limit{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Files Limit\"},{\"expr\":\"mysql_global_status_innodb_num_open_files{instance=~\\\"$instance\\\"}\",\"legend\":\"InnoDB Open Files\"}],\"name\":\"MySQL Open Files\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Table Openings", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n/\\n(\\nrate(mysql_global_status_table_open_cache_hits{instance=~\\\"$instance\\\"}[5m])\\n+\\nrate(mysql_global_status_table_open_cache_misses{instance=~\\\"$instance\\\"}[5m])\\n)\",\"legend\":\"Table Open Cache Hit Ratio\"}],\"name\":\"Table Open Cache Hit Ratio\",\"description\":\"**MySQL Table Open Cache Status**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"mysql_global_status_open_tables{instance=~\\\"$instance\\\"}\",\"legend\":\"Open Tables\"},{\"expr\":\"mysql_global_variables_table_open_cache{instance=~\\\"$instance\\\"}\",\"legend\":\"Table Open Cache\"}],\"name\":\"MySQL Open Tables\",\"description\":\"**MySQL Open Tables**\\n\\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\\n\\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/net_response_by_categraf.json b/etc/dashboards/net_response_by_categraf.json deleted file mode 100644 index 5f96e0705a0824976815da527762d701d345d662..0000000000000000000000000000000000000000 --- a/etc/dashboards/net_response_by_categraf.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "TCP探测", - "tags": "", - "configs": "", - "chart_groups": [ - { - "name": "Default chart group", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"max(net_response_result_code) by (target)\",\"legend\":\"UP?\"},{\"expr\":\"max(net_response_response_time) by (target)\",\"refId\":\"C\",\"legend\":\"latency(s)\"}],\"name\":\"Targets\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelValuesToRows\",\"aggrDimension\":\"target\"},\"options\":{\"valueMappings\":[],\"standardOptions\":{}},\"overrides\":[{\"properties\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"UP\",\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"special\":1,\"from\":1},\"result\":{\"text\":\"DOWN\",\"color\":\"#e90f0f\"}}],\"standardOptions\":{}},\"matcher\":{\"value\":\"A\"}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":4,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/oracle_by_categraf.json b/etc/dashboards/oracle_by_categraf.json deleted file mode 100644 index 3c6635baac7d96ed023c414c23d54414e69f4518..0000000000000000000000000000000000000000 --- a/etc/dashboards/oracle_by_categraf.json +++ /dev/null @@ -1,127 +0,0 @@ -[ - { - "name": "Oracle - 模板", - "tags": "Telegraf", - "configs": "{\"var\":[{\"name\":\"ident\",\"definition\":\"label_values(oracle_up,ident)\",\"options\":[\"tt-fc-log00.nj\"]},{\"name\":\"instance\",\"definition\":\"label_values(oracle_up{ident=\\\"$ident\\\"},instance)\"}]}", - "chart_groups": [ - { - "name": "Activities", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(oracle_activity_execute_count_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}[2m])\"}],\"name\":\"execute count / second\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"decimals\":1}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(oracle_activity_user_commits_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}[2m])\"}],\"name\":\"user commits / second\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(oracle_activity_user_rollbacks_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}[2m])\"}],\"name\":\"user rollbacks / second\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_up{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"status\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"text\":\"UP\",\"color\":\"#5ea70f\"}},{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"DOWN\",\"color\":\"#f60f0f\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Waits", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_wait_time_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"legend\":\"{{wait_class}}\"}],\"name\":\"Time waited\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Tablespace", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_tablespace_bytes{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}/oracle_tablespace_max_bytes{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"legend\":\"{{tablespace}}\"}],\"name\":\"Used Percent\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_tablespace_free{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"legend\":\"{{tablespace}}\"}],\"name\":\"Free space\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "IO and TPS", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_io_requests_per_second_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"IO Requests / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_user_transaction_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"TPS\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_io_megabytes_per_second_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}*1024*1024\"}],\"name\":\"IO Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Connections", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sessions_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\",status=\\\"ACTIVE\\\"}\",\"legend\":\"\"}],\"name\":\"Sessions\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "Hit Ratio", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_buffer_cache_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Buffer Cache Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_redo_allocation_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Redo Allocation Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_row_cache_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Row Cache Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_library_cache_hit_ratio_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"}],\"name\":\"Library Cache Hit Ratio\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Physical Read Write", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_bytes_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_Bytes_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical Read Write Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_total_bytes_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_Total_Bytes_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical Read Write Total Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_io_requests_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_IO_Requests_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical RW IO Requests / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"oracle_sysmetric_physical_read_total_io_requests_per_sec_value{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\"},{\"expr\":\"oracle_sysmetric_Physical_Write_Total_IO_Requests_Per_Sec{ident=\\\"$ident\\\", instance=\\\"$instance\\\"}\",\"refId\":\"B\"}],\"name\":\"Physical RW Total IO Requests / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/ping_by_categraf.json b/etc/dashboards/ping_by_categraf.json deleted file mode 100644 index 4c09be0c234d46462b274561f5fa96cf55b1e0a5..0000000000000000000000000000000000000000 --- a/etc/dashboards/ping_by_categraf.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "PING探测", - "tags": "", - "configs": "", - "chart_groups": [ - { - "name": "Default chart group", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"max(ping_result_code) by (target)\",\"legend\":\"UP?\"},{\"expr\":\"max(ping_percent_packet_loss) by (target)\",\"refId\":\"B\",\"legend\":\"Packet Loss %\"},{\"expr\":\"max(httpresponse_response_time) by (target)\",\"refId\":\"C\",\"legend\":\"latency(s)\"}],\"name\":\"Ping\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelValuesToRows\",\"aggrDimension\":\"target\"},\"options\":{\"valueMappings\":[],\"standardOptions\":{}},\"overrides\":[{\"properties\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"text\":\"UP\",\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"special\":1,\"from\":1},\"result\":{\"text\":\"DOWN\",\"color\":\"#e90f0f\"}}],\"standardOptions\":{}},\"matcher\":{\"value\":\"A\"}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":4,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/process_by_exporter.json b/etc/dashboards/process_by_exporter.json deleted file mode 100644 index 4a2d6c07ac5b912e8cae37ded22b7f8b1b1bef13..0000000000000000000000000000000000000000 --- a/etc/dashboards/process_by_exporter.json +++ /dev/null @@ -1,153 +0,0 @@ -[ - { - "name": "Linux Process - 模板", - "tags": "Prometheus Process", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(namedprocess_namegroup_num_procs, instance)\",\"multi\":false,\"options\":[\"tt-fc-es02.nj:12346\"]},{\"definition\":\"label_values(namedprocess_namegroup_cpu_seconds_total{instance=~\\\"$instance\\\"},groupname)\",\"name\":\"processes\",\"multi\":true,\"options\":[\"(sd-pam)\",\"NetworkManager\",\"YDLive\",\"YDPython\",\"YDService\",\"agent\",\"agetty\",\"atd\",\"auditd\",\"barad_agent\",\"bash\",\"chronyd\",\"crond\",\"dbus-daemon\",\"fc-agent\",\"fc-alert\",\"fc-checker\",\"gpg-agent\",\"less\",\"lsmd\",\"mongod\",\"mysql\",\"mysqld\",\"nginx\",\"ngo\",\"node\",\"openvpn\",\"podman pause\",\"polkitd\",\"process-agent\",\"redis-server\",\"rsyslogd\",\"sedispatch\",\"sgagent\",\"sh\",\"ssh-agent\",\"sshd\",\"sssd\",\"sssd_be\",\"sssd_nss\",\"su\",\"sudo\",\"systemd\",\"systemd-journal\",\"systemd-logind\",\"systemd-udevd\",\"tat_agent\",\"trace-agent\",\"tuned\",\"unbound-anchor\",\"vminsert-prod\",\"vmselect-prod\",\"vmstorage-prod\"],\"allOption\":true}]}", - "chart_groups": [ - { - "name": "Cpu Usage", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(rate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"user\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) + ignoring(mode) rate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])) or (irate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"user\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) + ignoring(mode) irate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Total CPU cores used\",\"description\":\"进程占用CPU时间(用户态+内核态),倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5, rate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or ( irate(namedprocess_namegroup_cpu_seconds_total{mode=\\\"system\\\",groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by System CPU cores used\",\"description\":\"进程占用CPU时间(内核态),倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percentUnit\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Memory Usage", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m])+ ignoring (memtype) avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m])) or (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m])+ ignoring (memtype) avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m])) ))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Used memory\",\"description\":\"进程常驻内存与交换空间平均占用容量之和,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5, (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"resident\\\",instance=~\\\"$instance\\\"}[5m]) ))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Resident Memory\",\"description\":\"进程常驻内存平均占用容量之和,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"swapped\\\",instance=~\\\"$instance\\\"}[5m]))) \",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Swapped Memory\",\"description\":\"进程交换内存平均占用容量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"virtual\\\",instance=~\\\"$instance\\\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\\\"$processes\\\", memtype=\\\"virtual\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Virtual Memory\",\"description\":\"进程虚拟内存平均占用容量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Disk IO Usage", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(rate(namedprocess_namegroup_write_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_write_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Bytes Written\",\"description\":\"进程写数据量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(rate(namedprocess_namegroup_read_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_read_bytes_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Bytes Read\",\"description\":\"进程读数据量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesSI\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Process and Thread Counts", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(max_over_time(namedprocess_namegroup_num_procs{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_num_procs{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by number of processes instances\",\"description\":\"同名进程数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(max_over_time(namedprocess_namegroup_num_threads{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_num_threads{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by number of threads\",\"description\":\"进程内线程数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Context Switches", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"voluntary\\\"}[5m]) or irate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"voluntary\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top Processes by Voluntary Context Switches\",\"description\":\"进程自愿中断(如I/O完成)次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"nonvoluntary\\\"}[5m]) or irate(namedprocess_namegroup_context_switches_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\",ctxswitchtype=\\\"nonvoluntary\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top Processes by Non-Voluntary Context Switches\",\"description\":\"进程被迫中断(如CPU时间耗尽)次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "File Descriptors", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,(max_over_time(namedprocess_namegroup_open_filedesc{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_open_filedesc{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Open File Descriptors\",\"description\":\"打开文件数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( max_over_time(namedprocess_namegroup_worst_fd_ratio{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or max_over_time(namedprocess_namegroup_worst_fd_ratio{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) ))*100\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by File Descriptor Usage Percent\",\"description\":\"已打开文件数与允许打开文件数占比,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"percent\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Page Faults", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_major_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_major_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Major Page Faults\",\"description\":\"主要页缺失次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( rate(namedprocess_namegroup_minor_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m]) or irate(namedprocess_namegroup_minor_page_faults_total{groupname=~\\\"$processes\\\",instance=~\\\"$instance\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top processes by Minor Page Faults\",\"description\":\"次要页缺失次数,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Statuses", - "weight": 7, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Running\\\"}[5m]) or max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Running\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top running processes\",\"description\":\"运行态同名进程数量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,( max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Waiting\\\"}[5m]) or max_over_time(namedprocess_namegroup_states{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\", state=\\\"Waiting\\\"}[5m])))\",\"legend\":\"{{groupname}}\"}],\"name\":\"Top of processes waiting on IO\",\"description\":\"等待IO状态同名进程数量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Kernel Waits(WCHAN)", - "weight": 8, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,sum(avg_over_time(namedprocess_namegroup_threads_wchan{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\"}[5m])) by (wchan) )\",\"legend\":\"{{wchan}}\"}],\"name\":\"Kernel waits for All\",\"description\":\"内核函数等待线程数量,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"topk(5,sum(avg_over_time(namedprocess_namegroup_threads_wchan{instance=~\\\"$instance\\\", groupname=~\\\"$processes\\\"}[5m])) by (wchan,groupname) )\",\"legend\":\"{{wchan}}\"}],\"name\":\"Kernel wait Details for All\",\"description\":\"内核函数等待线程数量按进程统计,倒排前5\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Uptime", - "weight": 9, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"time()-(namedprocess_namegroup_oldest_start_time_seconds{instance=~\\\"$instance\\\",groupname=~\\\"$processes\\\"}>0)\",\"legend\":\"{{groupname}}\"}],\"name\":\"Processes by uptime\",\"description\":\"进程启动时长\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"seriesToRows\"},\"options\":{\"standardOptions\":{\"util\":\"seconds\"}},\"overrides\":[{\"properties\":{\"standardOptions\":{\"util\":\"seconds\"},\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":1800},\"result\":{\"color\":\"#f91010\"}},{\"type\":\"range\",\"match\":{\"from\":1800},\"result\":{\"color\":\"#21f312\"}}]}}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/rabbitmq_by_categraf.json b/etc/dashboards/rabbitmq_by_categraf.json deleted file mode 100644 index fc384837606485f750fa2f74f3ca8371627ddd58..0000000000000000000000000000000000000000 --- a/etc/dashboards/rabbitmq_by_categraf.json +++ /dev/null @@ -1,221 +0,0 @@ -[ - { - "name": "RabbitMQ 3.8+", - "tags": "", - "configs": "{\"var\":[{\"definition\":\"label_values(rabbitmq_identity_info, rabbitmq_cluster)\",\"name\":\"rabbitmq_cluster\"}]}", - "chart_groups": [ - { - "name": "Overview", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_ready * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Ready messages\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10000},\"result\":{\"color\":\"#4a90e2\"}},{\"type\":\"range\",\"match\":{\"from\":100000},\"result\":{\"color\":\"#f50a0a\"}},{\"type\":\"range\",\"match\":{\"to\":9999},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":7,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Incoming messages / s\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":50},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":5,\"x\":7,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_channels * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) - sum(rabbitmq_channel_consumers * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Publishers\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_connections * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Connections\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queues * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Queues\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":0,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_unacked * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Unacknowledged messages\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":99},\"result\":{\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#4a90e2\"}},{\"type\":\"range\",\"match\":{\"from\":500},\"result\":{\"color\":\"#d0021b\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":7,\"x\":0,\"y\":1,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_redelivered_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_get_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\nsum(rate(rabbitmq_channel_get_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Outgoing messages / s\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":50},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":5,\"x\":7,\"y\":1,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_channel_consumers * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Consumers\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":1,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_channels * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Channels\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"from\":10},\"result\":{\"color\":\"#417505\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":1,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_build_info * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Nodes\",\"custom\":{\"textMode\":\"valueAndName\",\"colorMode\":\"background\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":null,\"from\":3},\"result\":{\"color\":\"#417505\"}},{\"type\":\"range\",\"match\":{\"from\":8},\"result\":{\"color\":\"#e70909\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":1,\"i\":\"9\"}}", - "weight": 0 - } - ] - }, - { - "name": "Nodes", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_build_info * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"nodes\",\"custom\":{\"showHeader\":true,\"calc\":\"lastNotNull\",\"displayMode\":\"labelsOfSeriesToRows\",\"columns\":[\"rabbitmq_cluster\",\"rabbitmq_node\",\"rabbitmq_version\",\"erlang_version\"]},\"options\":{\"standardOptions\":{}},\"overrides\":[{}],\"version\":\"2.0.0\",\"type\":\"table\",\"layout\":{\"h\":1,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"(rabbitmq_resident_memory_limit_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) -\\n(rabbitmq_process_resident_memory_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"Memory available before publishers blocked\",\"description\":\"If the value is zero or less, the memory alarm will be triggered and all publishing connections across all cluster nodes will be blocked.\\n\\nThis value can temporarily go negative because the memory alarm is triggered with a slight delay.\\n\\nThe kernel's view of the amount of memory used by the node can differ from what the node itself can observe. This means that this value can be negative for a sustained period of time.\\n\\nBy default nodes use resident set size (RSS) to compute how much memory they use. This strategy can be changed (see the guides below).\\n\\n* [Alarms](https://www.rabbitmq.com/alarms.html)\\n* [Memory Alarms](https://www.rabbitmq.com/memory.html)\\n* [Reasoning About Memory Use](https://www.rabbitmq.com/memory-use.html)\\n* [Blocked Connection Notifications](https://www.rabbitmq.com/connection-blocked.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":1,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_disk_space_available_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Disk space available before publishers blocked\",\"description\":\"This metric is reported for the partition where the RabbitMQ data directory is stored.\\n\\nIf the value is zero or less, the disk alarm will be triggered and all publishing connections across all cluster nodes will be blocked.\\n\\nThis value can temporarily go negative because the free disk space alarm is triggered with a slight delay.\\n\\n* [Alarms](https://www.rabbitmq.com/alarms.html)\\n* [Disk Space Alarms](https://www.rabbitmq.com/disk-alarms.html)\\n* [Disk Space](https://www.rabbitmq.com/production-checklist.html#resource-limits-disk-space)\\n* [Persistence Configuration](https://www.rabbitmq.com/persistence-conf.html)\\n* [Blocked Connection Notifications](https://www.rabbitmq.com/connection-blocked.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"(rabbitmq_process_max_fds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) -\\n(rabbitmq_process_open_fds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"File descriptors available\",\"description\":\"When this value reaches zero, new connections will not be accepted and disk write operations may fail.\\n\\nClient libraries, peer nodes and CLI tools will not be able to connect when the node runs out of available file descriptors.\\n\\n* [Open File Handles Limit](https://www.rabbitmq.com/production-checklist.html#resource-limits-file-handle-limit)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":8,\"x\":16,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"(rabbitmq_process_max_tcp_sockets * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) -\\n(rabbitmq_process_open_tcp_sockets * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\"}],\"name\":\"TCP sockets available\",\"description\":\"When this value reaches zero, new connections will not be accepted.\\n\\nClient libraries, peer nodes and CLI tools will not be able to connect when the node runs out of available file descriptors.\\n\\n* [Networking and RabbitMQ](https://www.rabbitmq.com/networking.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":8,\"x\":16,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - } - ] - }, - { - "name": "QUEUED MESSAGES", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_ready * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages ready to be delivered to consumers\",\"description\":\"Total number of ready messages ready to be delivered to consumers.\\n\\nAim to keep this value as low as possible. RabbitMQ behaves best when messages are flowing through it. It's OK for publishers to occasionally outpace consumers, but the expectation is that consumers will eventually process all ready messages.\\n\\nIf this metric keeps increasing, your system will eventually run out of memory and/or disk space. Consider using TTL or Queue Length Limit to prevent unbounded message growth.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\\n* [Queue Length Limit](https://www.rabbitmq.com/maxlength.html)\\n* [Time-To-Live and Expiration](https://www.rabbitmq.com/ttl.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rabbitmq_queue_messages_unacked * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages pending consumer acknowledgement\",\"description\":\"The total number of messages that are either in-flight to consumers, currently being processed by consumers or simply waiting for the consumer acknowledgements to be processed by the queue. Until the queue processes the message acknowledgement, the message will remain unacknowledged.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\\n* [Confirms and Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "INCOMING MESSAGES", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages published / s\",\"description\":\"The incoming message rate before any routing rules are applied.\\n\\nIf this value is lower than the number of messages published to queues, it may indicate that some messages are delivered to more than one queue.\\n\\nIf this value is higher than the number of messages published to queues, messages cannot be routed and will either be dropped or returned to publishers.\\n\\n* [Publishers](https://www.rabbitmq.com/publishers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_confirmed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages confirmed to publishers / s\",\"description\":\"The rate of messages confirmed by the broker to publishers. Publishers must opt-in to receive message confirmations.\\n\\nIf this metric is consistently at zero it may suggest that publisher confirms are not used by clients. The safety of published messages is likely to be at risk.\\n\\n* [Publisher Confirms](https://www.rabbitmq.com/confirms.html#publisher-confirms)\\n* [Publisher Confirms and Data Safety](https://www.rabbitmq.com/publishers.html#data-safety)\\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queue_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages routed to queues / s\",\"description\":\"The rate of messages received from publishers and successfully routed to the master queue replicas.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\\n* [Publishers](https://www.rabbitmq.com/publishers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_unconfirmed[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages unconfirmed to publishers / s\",\"description\":\"The rate of messages received from publishers that have publisher confirms enabled and the broker has not confirmed yet.\\n\\n* [Publishers](https://www.rabbitmq.com/publishers.html)\\n* [Confirms and Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_unroutable_dropped_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Unroutable messages dropped / s\",\"description\":\"The rate of messages that cannot be routed and are dropped. \\n\\nAny value above zero means message loss and likely suggests a routing problem on the publisher end.\\n\\n* [Unroutable Message Handling](https://www.rabbitmq.com/publishers.html#unroutable)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_unroutable_returned_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Unroutable messages returned to publishers / s\",\"description\":\"The rate of messages that cannot be routed and are returned back to publishers.\\n\\nSustained values above zero may indicate a routing problem on the publisher end.\\n\\n* [Unroutable Message Handling](https://www.rabbitmq.com/publishers.html#unroutable)\\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - } - ] - }, - { - "name": "OUTGOING MESSAGES", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(\\n (rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) +\\n (rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"})\\n) by(rabbitmq_node)\"}],\"name\":\"Messages delivered / s\",\"description\":\"The rate of messages delivered to consumers. It includes messages that have been redelivered.\\n\\nThis metric does not include messages that have been fetched by consumers using `basic.get` (consumed by polling).\\n\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_redelivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages redelivered / s\",\"description\":\"The rate of messages that have been redelivered to consumers. It includes messages that have been requeued automatically and redelivered due to channel exceptions or connection closures.\\n\\nHaving some redeliveries is expected, but if this metric is consistently non-zero, it is worth investigating why.\\n\\n* [Negative Acknowledgement and Requeuing of Deliveries](https://www.rabbitmq.com/confirms.html#consumer-nacks-requeue)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages delivered with manual ack / s\",\"description\":\"The rate of message deliveries to consumers that use manual acknowledgement mode.\\n\\nWhen this mode is used, RabbitMQ waits for consumers to acknowledge messages before more messages can be delivered.\\n\\nThis is the safest way of consuming messages.\\n\\n* [Consumer Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":1,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages delivered auto ack / s\",\"description\":\"The rate of message deliveries to consumers that use automatic acknowledgement mode.\\n\\nWhen this mode is used, RabbitMQ does not wait for consumers to acknowledge message deliveries.\\n\\nThis mode is fire-and-forget and does not offer any delivery safety guarantees. It tends to provide higher throughput and it may lead to consumer overload and higher consumer memory usage.\\n\\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":1,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_messages_acked_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Messages acknowledged / s\",\"description\":\"The rate of message acknowledgements coming from consumers that use manual acknowledgement mode.\\n\\n* [Consumer Acknowledgements](https://www.rabbitmq.com/confirms.html)\\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":2,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_get_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Polling operations with auto ack / s\",\"description\":\"The rate of messages delivered to polling consumers that use automatic acknowledgement mode.\\n\\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\\n\\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":2,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_get_empty_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Polling operations that yield no result / s\",\"description\":\"The rate of polling consumer operations that yield no result.\\n\\nAny value above zero means that RabbitMQ resources are wasted by polling consumers.\\n\\nCompare this metric to the other polling consumer metrics to see the inefficiency rate.\\n\\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\\n\\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":3,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channel_get_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Polling operations with manual ack / s\",\"description\":\"The rate of messages delivered to polling consumers that use manual acknowledgement mode.\\n\\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\\n\\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\\n* [Consumers](https://www.rabbitmq.com/consumers.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":12,\"y\":3,\"i\":\"7\"}}", - "weight": 0 - } - ] - }, - { - "name": "QUEUES", - "weight": 5, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_queues * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Total queues\",\"description\":\"Total number of queue masters per node. \\n\\nThis metric makes it easy to see sub-optimal queue distribution in a cluster.\\n\\n* [Queue Masters, Data Locality](https://www.rabbitmq.com/ha.html#master-migration-data-locality)\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queues_declared_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Queues declared / s\",\"description\":\"The rate of queue declarations performed by clients.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":4,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queues_created_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Queues created / s\",\"description\":\"The rate of new queues created (as opposed to redeclarations).\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":4,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_queues_deleted_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Queues deleted / s\",\"description\":\"The rate of queues deleted.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [Queues](https://www.rabbitmq.com/queues.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":4,\"x\":20,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "CHANNELS", - "weight": 6, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_channels * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Total channels\",\"description\":\"Total number of channels on all currently opened connections.\\n\\nIf this metric grows monotonically it is highly likely a channel leak in one of the applications. Confirm channel leaks by using the _Channels opened_ and _Channels closed_ metrics.\\n\\n* [Channel Leak](https://www.rabbitmq.com/channels.html#channel-leaks)\\n* [Channels](https://www.rabbitmq.com/channels.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channels_opened_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Channels opened / s\",\"description\":\"The rate of new channels opened by applications across all connections. Channels are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of channel churn or mass connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [High Channel Churn](https://www.rabbitmq.com/channels.html#high-channel-churn)\\n* [Channels](https://www.rabbitmq.com/channels.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_channels_closed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Channels closed / s\",\"description\":\"The rate of channels closed by applications across all connections. Channels are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of channel churn or mass connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\\n\\n* [High Channel Churn](https://www.rabbitmq.com/channels.html#high-channel-churn)\\n* [Channels](https://www.rabbitmq.com/channels.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "CONNECTIONS", - "weight": 7, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_connections_closed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Connections closed / s\",\"description\":\"The rate of connections closed. Connections are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of connection churn or mass connection recovery.\\n\\n* [Connections](https://www.rabbitmq.com/connections.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rabbitmq_connections * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}\"}],\"name\":\"Total connections\",\"description\":\"Total number of client connections.\\n\\nIf this metric grows monotonically it is highly likely a connection leak in one of the applications. Confirm connection leaks by using the _Connections opened_ and _Connections closed_ metrics.\\n\\n* [Connection Leak](https://www.rabbitmq.com/connections.html#monitoring)\\n* [Connections](https://www.rabbitmq.com/connections.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"sum(rate(rabbitmq_connections_opened_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\\\"$rabbitmq_cluster\\\"}) by(rabbitmq_node)\"}],\"name\":\"Connections opened / s\",\"description\":\"The rate of new connections opened by clients. Connections are expected to be long-lived.\\n\\nLow sustained values above zero are to be expected. High rates may be indicative of connection churn or mass connection recovery.\\n\\n* [Connection Leak](https://www.rabbitmq.com/connections.html#monitoring)\\n* [Connections](https://www.rabbitmq.com/connections.html)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/redis_by_categraf.json b/etc/dashboards/redis_by_categraf.json deleted file mode 100644 index 68dee4d3e622e685fab8950902926f0e6c12f10c..0000000000000000000000000000000000000000 --- a/etc/dashboards/redis_by_categraf.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "name": "Redis Overview - 模板", - "tags": "Redis Prometheus", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(redis_uptime_in_seconds,instance)\",\"selected\":\"10.206.0.16:6379\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(redis_uptime_in_seconds{instance=~\\\"$instance\\\"})\"}],\"name\":\"Redis Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_connected_clients{instance=~\\\"$instance\\\"})\"}],\"name\":\"Connected Clients\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_used_memory{instance=~\\\"$instance\\\"}\"}],\"name\":\"Memory Used\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":128000000},\"result\":{\"color\":\"#079e05\"}},{\"type\":\"range\",\"match\":{\"from\":128000000},\"result\":{\"color\":\"#f10909\"}}],\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_maxmemory{instance=~\\\"$instance\\\"}\"}],\"name\":\"Max Memory Limit\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(redis_total_commands_processed{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Commands Executed / sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(redis_keyspace_hits{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"hits\"},{\"expr\":\"irate(redis_keyspace_misses{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"misses\"}],\"name\":\"Hits / Misses per Sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"topk(5, irate(redis_cmdstat_calls{instance=~\\\"$instance\\\"} [1m]))\",\"legend\":\"{{command}}\"}],\"name\":\"Top Commands\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Keys", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum (redis_keyspace_keys{instance=~\\\"$instance\\\"}) by (db)\",\"legend\":\"{{db}}\"}],\"name\":\"Total Items per DB\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_expired_keys{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"expired\"},{\"expr\":\"sum(rate(redis_evicted_keys{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"evicted\"}],\"name\":\"Expired / Evicted\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_keyspace_keys{instance=~\\\"$instance\\\"}) - sum(redis_keyspace_expires{instance=~\\\"$instance\\\"}) \",\"legend\":\"not expiring\"},{\"expr\":\"sum(redis_keyspace_expires{instance=~\\\"$instance\\\"}) \",\"legend\":\"expiring\"}],\"name\":\"Expiring vs Not-Expiring Keys\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_total_net_input_bytes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"input\"},{\"expr\":\"sum(rate(redis_total_net_output_bytes{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"output\"}],\"name\":\"Network I/O\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/redis_by_exporter.json b/etc/dashboards/redis_by_exporter.json deleted file mode 100644 index 8e809019305d32d18983f5c0d4b5fb137783ef2a..0000000000000000000000000000000000000000 --- a/etc/dashboards/redis_by_exporter.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "name": "Redis Overview - 模板", - "tags": "Redis Prometheus", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(redis_uptime_in_seconds,instance)\",\"selected\":\"10.206.0.16:6379\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"min(redis_uptime_in_seconds{instance=~\\\"$instance\\\"})\"}],\"name\":\"Redis Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_connected_clients{instance=~\\\"$instance\\\"})\"}],\"name\":\"Connected Clients\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_memory_used_bytes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Memory Used\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":128000000},\"result\":{\"color\":\"#079e05\"}},{\"type\":\"range\",\"match\":{\"from\":128000000},\"result\":{\"color\":\"#f10909\"}}],\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"redis_memory_max_bytes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Max Memory Limit\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Commands", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"rate(redis_commands_processed_total{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"Commands Executed / sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(redis_keyspace_hits_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"hits\"},{\"expr\":\"irate(redis_keyspace_misses_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"misses\"}],\"name\":\"Hits / Misses per Sec\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"topk(5, irate(redis_commands_total{instance=~\\\"$instance\\\"} [1m]))\",\"legend\":\"{{cmd}}\"}],\"name\":\"Top Commands\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Keys", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum (redis_db_keys{instance=~\\\"$instance\\\"}) by (db)\",\"legend\":\"{{db}}\"}],\"name\":\"Total Items per DB\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_expired_keys_total{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"expired\"},{\"expr\":\"sum(rate(redis_evicted_keys_total{instance=~\\\"$instance\\\"}[5m])) by (instance)\",\"legend\":\"evicted\"}],\"name\":\"Expired / Evicted\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"sum(redis_db_keys{instance=~\\\"$instance\\\"}) - sum(redis_db_keys_expiring{instance=~\\\"$instance\\\"}) \",\"legend\":\"not expiring\"},{\"expr\":\"sum(redis_db_keys_expiring{instance=~\\\"$instance\\\"}) \",\"legend\":\"expiring\"}],\"name\":\"Expiring vs Not-Expiring Keys\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"noraml\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"sum(rate(redis_net_input_bytes_total{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"input\"},{\"expr\":\"sum(rate(redis_net_output_bytes_total{instance=~\\\"$instance\\\"}[5m]))\",\"legend\":\"output\"}],\"name\":\"Network I/O\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/tomcat_by_categraf.json b/etc/dashboards/tomcat_by_categraf.json deleted file mode 100644 index be18e93dbbe898b2212cbb17b0cd28459c622eb5..0000000000000000000000000000000000000000 --- a/etc/dashboards/tomcat_by_categraf.json +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "name": "Tomcat - 模板", - "tags": "Categraf", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(tomcat_up, instance)\"}],\"links\":[{\"title\":\"n9e\",\"url\":\"https://n9e.gitee.io/\",\"targetBlank\":true}]}", - "chart_groups": [ - { - "name": "connector", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(tomcat_connector_bytes_sent{instance=\\\"$instance\\\"}[1m])\",\"legend\":\"sent\"},{\"expr\":\"rate(tomcat_connector_bytes_received{instance=\\\"$instance\\\"}[1m])\",\"refId\":\"B\",\"legend\":\"received\"}],\"name\":\"Traffic Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\"},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(tomcat_connector_request_count{instance=\\\"$instance\\\"}[1m])\",\"legend\":\"tomcat_connector_request_count\"},{\"expr\":\"rate(tomcat_connector_error_count{instance=\\\"$instance\\\"}[1m])\",\"refId\":\"B\",\"legend\":\"tomcat_connector_error_count\"}],\"name\":\"Request count / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_connector_max_threads{instance=\\\"$instance\\\"}\",\"legend\":\"max_threads\"},{\"expr\":\"tomcat_connector_current_thread_count{instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"current_thread_count\"},{\"expr\":\"tomcat_connector_current_threads_busy{instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"current_threads_busy\"}],\"name\":\"Tread\",\"description\":\"max_threads: The maximum number of allowed worker threads.\\ncurrent_thread_count: The number of threads managed by the thread pool\\ncurrent_threads_busy: The number of threads that are in use\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":2,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(tomcat_connector_processing_time{instance=\\\"$instance\\\"}[1m])\",\"legend\":\"{{name}}-processing_time\"}],\"name\":\"Processing time\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":2,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "mem used", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memory_max{instance=\\\"$instance\\\"}\",\"legend\":\"max\"},{\"expr\":\"tomcat_jvm_memory_total{instance=\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"used\"},{\"expr\":\"tomcat_jvm_memory_free{instance=\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"free\"}],\"name\":\"Mem Used\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - } - ] - }, - { - "name": "memorypool", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_used{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Used\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_max{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Max\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_committed{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Committed\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"tomcat_jvm_memorypool_init{instance=\\\"$instance\\\"}\",\"legend\":\"{{name}}\"}],\"name\":\"Init\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/windows_by_exporter.json b/etc/dashboards/windows_by_exporter.json deleted file mode 100644 index df60a95bcda14a7974e7d1fa006b14cf153839ee..0000000000000000000000000000000000000000 --- a/etc/dashboards/windows_by_exporter.json +++ /dev/null @@ -1,99 +0,0 @@ -[ - { - "name": "Windows - 模板", - "tags": "Windows Prometheus", - "configs": "{\"var\":[{\"name\":\"instance\",\"definition\":\"label_values(windows_system_system_up_time, instance)\"}]}", - "chart_groups": [ - { - "name": "Basic Info", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"time() - windows_system_system_up_time{instance=~\\\"$instance\\\"}\",\"legend\":\"\"}],\"name\":\"Uptime\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"humantimeSeconds\"}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_cs_logical_processors{instance=~\\\"$instance\\\"}\",\"legend\":\"\"}],\"name\":\"CPU Core Total\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_cs_physical_memory_bytes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Memory Total\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_os_processes{instance=~\\\"$instance\\\"}\"}],\"name\":\"Process Total\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{}},\"options\":{\"valueMappings\":[{\"type\":\"range\",\"match\":{\"to\":100},\"result\":{\"color\":\"#109d06\"}},{\"type\":\"range\",\"match\":{\"from\":100},\"result\":{\"color\":\"#d11010\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "CPU Memory Disk", - "weight": 1, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"100 * sum by (instance) (rate(windows_cpu_time_total{mode != 'idle'}[5m])) / count by (instance) (windows_cpu_core_frequency_mhz) \",\"legend\":\"CPU Util\"}],\"name\":\"Cpu Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"100 - (windows_os_physical_memory_free_bytes{instance=~\\\"$instance\\\"} / windows_cs_physical_memory_bytes{instance=~\\\"$instance\\\"})*100\"}],\"name\":\"Memory Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{\"steps\":[{\"value\":70,\"color\":\"#e71313\"}]}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"100 - (windows_logical_disk_free_bytes{instance=~\\\"$instance\\\"} / windows_logical_disk_size_bytes{instance=~\\\"$instance\\\"})*100\",\"legend\":\"{{volume}}\"}],\"name\":\"Disk Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"windows_logical_disk_free_bytes{instance=~\\\"$instance\\\"}\",\"legend\":\"{{volume}} Free\"},{\"expr\":\"windows_logical_disk_size_bytes{instance=~\\\"$instance\\\"}\",\"legend\":\"{{volume}} Total\"}],\"name\":\"Disk Free\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":0},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - } - ] - }, - { - "name": "Disk IO", - "weight": 2, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_logical_disk_read_bytes_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Read\"},{\"expr\":\"irate(windows_logical_disk_write_bytes_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Write\"}],\"name\":\"Read/Write Bytes / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bytesIEC\",\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_logical_disk_reads_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Read\"},{\"expr\":\"irate(windows_logical_disk_writes_total{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{volume}} Write\"}],\"name\":\"Read/Write / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":2},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - }, - { - "name": "Network", - "weight": 3, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_net_bytes_sent_total{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}[5m])*8\",\"legend\":\"{{nic}} Sent\"},{\"expr\":\"irate(windows_net_bytes_received_total{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}[5m])*8\",\"legend\":\"{{nic}} Received\"}],\"name\":\"Sent/Received bits / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"util\":\"bitsIEC\",\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"(irate(windows_net_bytes_total{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}[5m]) * 8 / windows_net_current_bandwidth{instance=~\\\"$instance\\\",nic!~'isatap.*|VPN.*'}) * 100\"}],\"name\":\"Network Util\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{\"decimals\":1},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_net_packets_outbound_discarded{instance=~\\\"$instance\\\", nic!~'isatap.*|VPN.*'}[5m]) + irate(windows_net_packets_outbound_errors{instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"outbound\"},{\"expr\":\"irate(windows_net_packets_received_discarded{job=~\\\"$job\\\",instance=~\\\"$instance\\\", nic!~'isatap.*|VPN.*'}[5m]) + irate(windows_net_packets_received_errors{job=~\\\"$job\\\",instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"received\"}],\"name\":\"Packets / Second\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - } - ] - }, - { - "name": "System", - "weight": 4, - "charts": [ - { - "configs": "{\"targets\":[{\"expr\":\"windows_system_threads{instance=~\\\"$instance\\\"}\"}],\"name\":\"System Threads\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"expr\":\"irate(windows_system_exception_dispatches_total{instance=~\\\"$instance\\\"}[5m])\"}],\"name\":\"System exception dispatches\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"desc\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/dashboards/zookeeper_by_exporter.json b/etc/dashboards/zookeeper_by_exporter.json deleted file mode 100644 index 842f1f1554803463233986b6c8aca2c5c69d76fe..0000000000000000000000000000000000000000 --- a/etc/dashboards/zookeeper_by_exporter.json +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "name": "Zookeeper - 模板", - "tags": "", - "configs": "{\"var\":[{\"name\":\"job\",\"definition\":\"label_values(zk_up,job)\"},{\"definition\":\"label_values(zk_up,instance)\",\"name\":\"instance\"}]}", - "chart_groups": [ - { - "name": "overview", - "weight": 0, - "charts": [ - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_up{job=\\\"$job\\\", instance=\\\"$instance\\\"}\",\"legend\":\"up\"}],\"name\":\"up\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":40}},\"options\":{\"valueMappings\":[{\"type\":\"special\",\"match\":{\"special\":1},\"result\":{\"color\":\"#3d950e\",\"text\":\"UP\"}},{\"type\":\"special\",\"match\":{\"special\":0},\"result\":{\"color\":\"#f01414\",\"text\":\"DOWN\"}}],\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_znode_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"zk_znode_count\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_watch_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"zk_watch_count\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_ephemerals_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"zk_ephemerals_count\"}],\"name\":\"zk_ephemerals_count\",\"custom\":{\"textMode\":\"value\",\"colorMode\":\"value\",\"calc\":\"lastNotNull\",\"colSpan\":1,\"textSize\":{\"value\":50}},\"options\":{\"standardOptions\":{}},\"version\":\"2.0.0\",\"type\":\"stat\",\"layout\":{\"h\":1,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"rate(zk_packets_sent{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}[5m])\",\"legend\":\"{{instance}}-sent\"},{\"expr\":\"rate(zk_packets_received{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}[5m])\",\"refId\":\"B\",\"legend\":\"{{instance}}-received\"}],\"name\":\"Pakages\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":1,\"i\":\"4\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_num_alive_connections{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"alive_connections\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":3,\"i\":\"5\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_open_file_descriptor_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}-open\"},{\"expr\":\"zk_max_file_descriptor_count{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"{{instance}}-max\"}],\"name\":\"file_descriptor\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":3,\"i\":\"6\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_avg_latency{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}-avg\"},{\"expr\":\"zk_min_latency{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"refId\":\"B\",\"legend\":\"{{instance}}-min\"},{\"expr\":\"zk_max_latency{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"refId\":\"C\",\"legend\":\"{{instance}}-max\"}],\"name\":\"latency(ms)\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":3,\"i\":\"7\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_outstanding_requests{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"outstanding_requests\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":3,\"i\":\"8\"}}", - "weight": 0 - }, - { - "configs": "{\"targets\":[{\"refId\":\"A\",\"expr\":\"zk_approximate_data_size{job=~\\\"$job\\\", instance=~\\\"$instance\\\"}\",\"legend\":\"{{instance}}\"}],\"name\":\"approximate_data_size\",\"options\":{\"tooltip\":{\"mode\":\"all\",\"sort\":\"none\"},\"legend\":{\"displayMode\":\"hidden\"},\"standardOptions\":{},\"thresholds\":{}},\"custom\":{\"drawStyle\":\"lines\",\"lineInterpolation\":\"smooth\",\"fillOpacity\":0.5,\"stack\":\"off\"},\"version\":\"2.0.0\",\"type\":\"timeseries\",\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":1,\"i\":\"9\"}}", - "weight": 0 - } - ] - } - ] - } - ] \ No newline at end of file diff --git a/etc/event.json b/etc/event.json new file mode 100644 index 0000000000000000000000000000000000000000..627dc6ebbcfe1931ddfc78a86046b03c1bc55da4 --- /dev/null +++ b/etc/event.json @@ -0,0 +1,42 @@ +{ + "id": 1000000, + "cate": "prometheus", + "cluster": "", + "datasource_id": 3, + "group_id": 1, + "group_name": "Default Busi Group", + "hash": "2cb966f9ba1cdc7af94c3796e855955a", + "rule_id": 23, + "rule_name": "测试", + "rule_note": "测试", + "rule_prod": "metric", + "rule_algo": "", + "severity": 0, + "prom_for_duration": 60, + "prom_ql": "", + "prom_eval_interval": 30, + "callbacks": [], + "runbook_url": "", + "notify_recovered": 1, + "notify_channels": [], + "notify_groups": [], + "notify_groups_obj": null, + "target_ident": "", + "target_note": "", + "trigger_time": 1677229517, + "trigger_value": "2273533952", + "tags": [ + "__name__=disk_free", + "dc=qcloud-dev", + "device=vda1", + "fstype=ext4", + "ident=tt-fc-dev00.nj", + "rulename=测试" + ], + "is_recovered": false, + "notify_users_obj": null, + "last_eval_time": 1677229517, + "last_sent_time": 1677229517, + "notify_cur_number": 1, + "first_trigger_time": 1677229517 +} \ No newline at end of file diff --git a/etc/i18n.json b/etc/i18n.json new file mode 100644 index 0000000000000000000000000000000000000000..3bc944508c92298524cfa8e0b19030b51837f0a5 --- /dev/null +++ b/etc/i18n.json @@ -0,0 +1,49 @@ +{ + "zh": { + "Username or password invalid": "用户名或密码错误", + "roles empty": "角色不能为空", + "Username already exists": "此用户名已存在 请使用其他用户名", + "failed to count user-groups": "校验数据失败 请重试", + "UserGroup already exists": "组名已存在 请使用其他名称", + "members empty": "成员不能为空", + "At least one team have rw permission": "至少需要有一个团队有读写权限", + "Failed to create BusiGroup(%s)": "[%s]创建失败 请重试", + "business group id invalid": "业务组 id 不正确", + "idents empty": "监控对象不能为空", + "invalid tag(%s)": "tag不合法[%s]", + "invalid tagkey(%s): cannot contains . ": "tagkey[%s]不能包含.", + "invalid tagkey(%s): cannot contains _ ": "tagkey[%s]不能包含_", + "invalid tagkey(%s)": "tagkey不合法[%s]", + "duplicate tagkey(%s)":"tagkey(%s)重复了", + "name is empty": "名称不能为空", + "Ident duplicate":"大盘唯一标识已存在", + "No such dashboard":"大盘不存在", + "Name has invalid characters":"名称包含非法字符", + "Name is blank":"名称不能为空", + "forbidden":"没有权限", + "builtin alerts is empty, file: %s":"内置告警模板为空 %s", + "input json is empty":"提交内容不能为空", + "fields empty":"选择字段不能为空", + "No such AlertRule":"无此告警规则", + "GroupId(%d) invalid":"业务组id无效", + "No such recording rule":"无此记录规则", + "tags is blank":"标签不能为空", + "oops... etime(%d) <= btime(%d)":"开始时间,不能大于结束时间", + "group_id invalid":"业务组无效", + "No such AlertMute":"无此屏蔽规则", + "rule_id and tags are both blank":"告警规则和标签不能同时为空", + "rule is blank":"规则不能为空", + "rule invalid":"规则无效 请检查是否正确", + "unsupported field: %s":"不支持字段 %s", + "arg(batch) should be nonnegative":"batch 不能为负数", + "arg(tolerance) should be nonnegative":"tolerance 不能为负数", + "arg(timeout) should be nonnegative":"timeout 不能为负数", + "arg(timeout) longer than one day":"timeout 时间不能超过1天", + "arg(title) is required":"title 为必填项", + "created task.id is zero":"任务id为零", + "invalid ibex address: %s":"ibex %s 地址无效", + "url path invalid":"url非法", + "no such server":"无此实例", + "admin role can not be modified":"管理员角色不允许修改" + } +} \ No newline at end of file diff --git a/etc/ops.yaml b/etc/ops.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2d0a1237724e31f0e46f2ded8c6949d808a43b0d --- /dev/null +++ b/etc/ops.yaml @@ -0,0 +1,110 @@ +ops: +- name: dashboards + cname: 仪表盘 + ops: + - "/dashboards" + - "/dashboards/add" + - "/dashboards/put" + - "/dashboards/del" + - "/dashboards-built-in" + +- name: alert + cname: 告警规则 + ops: + - "/alert-rules" + - "/alert-rules/add" + - "/alert-rules/put" + - "/alert-rules/del" + - "/alert-rules-built-in" +- name: alert-mutes + cname: 告警静默管理 + ops: + - "/alert-mutes" + - "/alert-mutes/add" + - "/alert-mutes/del" + +- name: alert-subscribes + cname: 告警订阅管理 + ops: + - "/alert-subscribes" + - "/alert-subscribes/add" + - "/alert-subscribes/put" + - "/alert-subscribes/del" + +- name: alert-events + cname: 告警事件管理 + ops: + - "/alert-cur-events" + - "/alert-cur-events/del" + - "/alert-his-events" + +- name: recording-rules + cname: 记录规则管理 + ops: + - "/recording-rules" + - "/recording-rules/add" + - "/recording-rules/put" + - "/recording-rules/del" + +- name: metric + cname: 时序指标 + ops: + - "/metric/explorer" + - "/object/explorer" + +- name: log + cname: 日志分析 + ops: + - "/log/explorer" + +- name: trace + cname: 链路追踪 + ops: + - "/trace/explorer" + - "/trace/dependencies" + +- name: targets + cname: 基础设施 + ops: + - "/targets" + - "/targets/add" + - "/targets/put" + - "/targets/del" + +- name: job + cname: 任务管理 + ops: + - "/job-tpls" + - "/job-tpls/add" + - "/job-tpls/put" + - "/job-tpls/del" + - "/job-tasks" + - "/job-tasks/add" + - "/job-tasks/put" + +- name: user + cname: 用户管理 + ops: + - "/users" + - "/user-groups" + - "/user-groups/add" + - "/user-groups/put" + - "/user-groups/del" + +- name: busi-groups + cname: 业务分组管理 + ops: + - "/busi-groups" + - "/busi-groups/add" + - "/busi-groups/put" + - "/busi-groups/del" + +- name: system + cname: 系统信息 + ops: + - "/help/version" + - "/help/servers" + - "/help/source" + - "/help/sso" + - "/help/notification-tpls" + - "/help/notification-settings" \ No newline at end of file diff --git a/etc/script/notify.bak.py b/etc/script/notify.bak.py index bd240cbda167779abb1388a0bba4eaeb33bff2de..5a7fa68215b101052669ba38fb0a97e853152acb 100644 --- a/etc/script/notify.bak.py +++ b/etc/script/notify.bak.py @@ -42,7 +42,7 @@ class Sender(object): return recipients = emails.keys() - mail_body = payload.get('tpls').get("mailbody.tpl", "mailbody.tpl not found") + mail_body = payload.get('tpls').get("email.tpl", "email.tpl not found") message = MIMEText(mail_body, 'html', 'utf-8') message['From'] = mail_from message['To'] = ", ".join(recipients) diff --git a/etc/script/notify.py b/etc/script/notify.py index d57bdfa606b0f05c069a03608a9b41749209696c..2a2ae83b05c22830a2f93eb6c4e20d7a901b2767 100755 --- a/etc/script/notify.py +++ b/etc/script/notify.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import sys import json @@ -70,4 +70,4 @@ if __name__ == "__main__": elif sys.argv[1] == "hello": hello() else: - print("I am confused") \ No newline at end of file + print("I am confused") diff --git a/etc/server.conf b/etc/server.conf deleted file mode 100644 index 8e3547d7cb1639aee861f90b1b33b149a50fd9ac..0000000000000000000000000000000000000000 --- a/etc/server.conf +++ /dev/null @@ -1,235 +0,0 @@ -# debug, release -RunMode = "release" - -# my cluster name -ClusterName = "Default" - -# Default busigroup Key name -# do not change -BusiGroupLabelKey = "busigroup" - -# sleep x seconds, then start judge engine -EngineDelay = 30 - -DisableUsageReport = true - -# config | database -ReaderFrom = "config" - -# if true, target tags can rewrite labels defined in categraf config file -LabelRewrite = false - -[Log] -# log write dir -Dir = "logs" -# log level: DEBUG INFO WARNING ERROR -Level = "INFO" -# stdout, stderr, file -Output = "stdout" -# # rotate by time -# KeepHours: 4 -# # rotate by size -# RotateNum = 3 -# # unit: MB -# RotateSize = 256 - -[HTTP] -# http listening address -Host = "0.0.0.0" -# http listening port -Port = 19000 -# https cert file path -CertFile = "" -# https key file path -KeyFile = "" -# whether print access log -PrintAccessLog = false -# whether enable pprof -PProf = false -# http graceful shutdown timeout, unit: s -ShutdownTimeout = 30 -# max content length: 64M -MaxContentLength = 67108864 -# http server read timeout, unit: s -ReadTimeout = 20 -# http server write timeout, unit: s -WriteTimeout = 40 -# http server idle timeout, unit: s -IdleTimeout = 120 - -# [BasicAuth] -# user002 = "ccc26da7b9aba533cbb263a36c07dcc9" - -[Heartbeat] -# auto detect if blank -IP = "" -# unit ms -Interval = 1000 - -[SMTP] -Host = "smtp.163.com" -Port = 994 -User = "username" -Pass = "password" -From = "username@163.com" -InsecureSkipVerify = true -Batch = 5 - -[Alerting] -# timeout settings, unit: ms, default: 30000ms -Timeout=30000 -TemplatesDir = "./etc/template" -NotifyConcurrency = 10 -# use builtin go code notify -NotifyBuiltinChannels = ["email", "dingtalk", "wecom", "feishu", "mm", "telegram"] - -[Alerting.CallScript] -# built in sending capability in go code -# so, no need enable script sender -Enable = false -ScriptPath = "./etc/script/notify.py" - -[Alerting.CallPlugin] -Enable = false -# use a plugin via `go build -buildmode=plugin -o notify.so` -PluginPath = "./etc/script/notify.so" -# The first letter must be capitalized to be exported -Caller = "N9eCaller" - -[Alerting.RedisPub] -Enable = false -# complete redis key: ${ChannelPrefix} + ${Cluster} -ChannelPrefix = "/alerts/" - -[Alerting.Webhook] -Enable = false -Url = "http://a.com/n9e/callback" -BasicAuthUser = "" -BasicAuthPass = "" -Timeout = "5s" -Headers = ["Content-Type", "application/json", "X-From", "N9E"] - -[NoData] -Metric = "target_up" -# unit: second -Interval = 120 - -[Ibex] -# callback: ${ibex}/${tplid}/${host} -Address = "127.0.0.1:10090" -# basic auth -BasicAuthUser = "ibex" -BasicAuthPass = "ibex" -# unit: ms -Timeout = 3000 - -[Redis] -# address, ip:port or ip1:port,ip2:port for cluster and sentinel(SentinelAddrs) -Address = "127.0.0.1:6379" -# Username = "" -# Password = "" -# DB = 0 -# UseTLS = false -# TLSMinVersion = "1.2" -# standalone cluster sentinel -RedisType = "standalone" -# Mastername for sentinel type -# MasterName = "mymaster" -# SentinelUsername = "" -# SentinelPassword = "" - -[DB] -# postgres: host=%s port=%s user=%s dbname=%s password=%s sslmode=%s -DSN="root:1234@tcp(127.0.0.1:3306)/n9e_v5?charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true" -# enable debug mode or not -Debug = false -# mysql postgres -DBType = "mysql" -# unit: s -MaxLifetime = 7200 -# max open connections -MaxOpenConns = 150 -# max idle connections -MaxIdleConns = 50 -# table prefix -TablePrefix = "" -# enable auto migrate or not -# EnableAutoMigrate = false - -[Reader] -# prometheus base url -Url = "http://127.0.0.1:9090" -# Basic auth username -BasicAuthUser = "" -# Basic auth password -BasicAuthPass = "" -# timeout settings, unit: ms -Timeout = 30000 -DialTimeout = 3000 -MaxIdleConnsPerHost = 100 - -# [[Readers]] -# ClusterName = "Default" -# prometheus base url -# Url = "http://127.0.0.1:9090" -# Basic auth username -# BasicAuthUser = "" -# Basic auth password -# BasicAuthPass = "" -# timeout settings, unit: ms -# Timeout = 30000 -# DialTimeout = 3000 -# MaxIdleConnsPerHost = 100 - -[WriterOpt] -# queue channel count -QueueCount = 1000 -# queue max size -QueueMaxSize = 1000000 -# once pop samples number from queue -QueuePopSize = 1000 -# metric or ident -ShardingKey = "ident" - -[[Writers]] -Url = "http://127.0.0.1:9090/api/v1/write" -# Basic auth username -BasicAuthUser = "" -# Basic auth password -BasicAuthPass = "" -# timeout settings, unit: ms -Headers = ["X-From", "n9e"] -Timeout = 10000 -DialTimeout = 3000 -TLSHandshakeTimeout = 30000 -ExpectContinueTimeout = 1000 -IdleConnTimeout = 90000 -# time duration, unit: ms -KeepAlive = 30000 -MaxConnsPerHost = 0 -MaxIdleConns = 100 -MaxIdleConnsPerHost = 100 -# [[Writers.WriteRelabels]] -# Action = "replace" -# SourceLabels = ["__address__"] -# Regex = "([^:]+)(?::\\d+)?" -# Replacement = "$1:80" -# TargetLabel = "__address__" - -# [[Writers]] -# Url = "http://127.0.0.1:7201/api/v1/prom/remote/write" -# # Basic auth username -# BasicAuthUser = "" -# # Basic auth password -# BasicAuthPass = "" -# # timeout settings, unit: ms -# Timeout = 30000 -# DialTimeout = 10000 -# TLSHandshakeTimeout = 30000 -# ExpectContinueTimeout = 1000 -# IdleConnTimeout = 90000 -# # time duration, unit: ms -# KeepAlive = 30000 -# MaxConnsPerHost = 0 -# MaxIdleConns = 100 -# MaxIdleConnsPerHost = 100 diff --git a/etc/service/n9e-server.service b/etc/service/n9e-server.service deleted file mode 100644 index 9457d3da4ceab49066acb4d1231820c8849692bb..0000000000000000000000000000000000000000 --- a/etc/service/n9e-server.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description="n9e-server" -After=network.target - -[Service] -Type=simple - -ExecStart=/root/gopath/src/n9e/n9e server -WorkingDirectory=/root/gopath/src/n9e - -Restart=on-failure -SuccessExitStatus=0 -LimitNOFILE=65536 -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=n9e-server - - -[Install] -WantedBy=multi-user.target diff --git a/etc/service/n9e-webapi.service b/etc/service/n9e-webapi.service deleted file mode 100644 index 8669ceccaf53a8560aeda0626104523a77169ff7..0000000000000000000000000000000000000000 --- a/etc/service/n9e-webapi.service +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description="n9e-webapi" -After=network.target - -[Service] -Type=simple - -ExecStart=/root/gopath/src/n9e/n9e webapi -WorkingDirectory=/root/gopath/src/n9e - -Restart=on-failure -SuccessExitStatus=0 -LimitNOFILE=65536 -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=n9e-webapi - - -[Install] -WantedBy=multi-user.target diff --git a/etc/service/telegraf.service b/etc/service/telegraf.service deleted file mode 100644 index b73ef2acace29962fe2218bf58dad209cbad15a9..0000000000000000000000000000000000000000 --- a/etc/service/telegraf.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description="telegraf" -After=network.target - -[Service] -Type=simple - -ExecStart=/opt/telegraf/usr/bin/telegraf --config etc/telegraf/telegraf.conf --output-filter opentsdb -WorkingDirectory=/opt/telegraf - -KillMode=process -KillSignal=SIGQUIT -TimeoutStopSec=5 -Restart=always -SuccessExitStatus=0 -LimitNOFILE=65536 -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=telegraf - - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/etc/template/mailbody.tpl b/etc/template/email.tpl similarity index 100% rename from etc/template/mailbody.tpl rename to etc/template/email.tpl diff --git a/docker/n9eetc/template/subject.tpl b/etc/template/mailsubject.tpl similarity index 100% rename from docker/n9eetc/template/subject.tpl rename to etc/template/mailsubject.tpl diff --git a/etc/template/subject.tpl b/etc/template/subject.tpl deleted file mode 100644 index ec241bc38a5556a37be843c108dbadcc6c7b0053..0000000000000000000000000000000000000000 --- a/etc/template/subject.tpl +++ /dev/null @@ -1 +0,0 @@ -{{if .IsRecovered}}Recovered{{else}}Triggered{{end}}: {{.RuleName}} {{.TagsJSON}} \ No newline at end of file diff --git a/fe.sh b/fe.sh deleted file mode 100755 index 8a21487fd842aa64c28f93acfaf254cdd270c3d9..0000000000000000000000000000000000000000 --- a/fe.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -TAG=$(curl -sX GET https://api.github.com/repos/n9e/fe-v5/releases/latest | awk '/tag_name/{print $4;exit}' FS='[""]') -VERSION=$(echo $TAG | sed 's/v//g') - -curl -o n9e-fe-${VERSION}.tar.gz -L https://github.com/n9e/fe-v5/releases/download/${TAG}/n9e-fe-${VERSION}.tar.gz - -tar zxvf n9e-fe-${VERSION}.tar.gz diff --git a/go.mod b/go.mod index d3dcde0aff8c0e7d395e9f20cd9418daa4028c7c..384860539c0cee4dc9076aeaf93485e10c464efc 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,13 @@ -module github.com/didi/nightingale/v5 +module github.com/ccfos/nightingale/v6 go 1.18 require ( github.com/coreos/go-oidc v2.2.1+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/gin-contrib/pprof v1.3.0 - github.com/gin-gonic/gin v1.7.4 - github.com/go-ldap/ldap/v3 v3.4.1 - github.com/go-redis/redis/v9 v9.0.0-rc.1 + github.com/gin-contrib/pprof v1.4.0 + github.com/gin-gonic/gin v1.8.2 + github.com/go-ldap/ldap/v3 v3.4.4 github.com/gogo/protobuf v1.3.2 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/protobuf v1.5.2 @@ -17,71 +16,69 @@ require ( github.com/json-iterator/go v1.1.12 github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/mailru/easyjson v0.7.7 - github.com/mattn/go-isatty v0.0.12 - github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc + github.com/mattn/go-isatty v0.0.16 + github.com/pelletier/go-toml/v2 v2.0.6 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.12.2 - github.com/prometheus/common v0.32.1 + github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/common v0.39.0 github.com/prometheus/prometheus v2.5.0+incompatible + github.com/redis/go-redis/v9 v9.0.2 github.com/tidwall/gjson v1.14.0 - github.com/toolkits/pkg v1.3.1-0.20220824084030-9f9f830a05d5 - github.com/urfave/cli/v2 v2.3.0 - golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c + github.com/toolkits/pkg v1.3.3 + golang.org/x/oauth2 v0.3.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - gorm.io/driver/mysql v1.1.2 - gorm.io/driver/postgres v1.1.1 - gorm.io/gorm v1.21.15 + gorm.io/driver/mysql v1.4.4 + gorm.io/driver/postgres v1.4.5 + gorm.io/gorm v1.24.2 ) require ( - github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect + github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/BurntSushi/toml v0.3.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/go-playground/validator/v10 v10.4.1 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.11.1 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/goccy/go-json v0.9.11 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.10.0 // indirect + github.com/jackc/pgconn v1.13.0 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgproto3/v2 v2.3.1 // indirect github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.8.1 // indirect - github.com/jackc/pgx/v4 v4.13.0 // indirect + github.com/jackc/pgtype v1.12.0 // indirect + github.com/jackc/pgx/v4 v4.17.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.2 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/leodido/go-urn v1.2.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect - github.com/ugorji/go/codec v1.1.7 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect go.uber.org/automaxprocs v1.4.0 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4 // indirect - google.golang.org/grpc v1.41.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 27375db6fd0a583369741dbbcee22bda076c4800..bfa5e30e65ece5a92e9451c9f546b051d3159ae9 100644 --- a/go.sum +++ b/go.sum @@ -1,76 +1,29 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= -github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bsm/ginkgo/v2 v2.5.0 h1:aOAnND1T40wEdAtkGSkvSICWeQ8L3UASX7YVCqQx+eQ= +github.com/bsm/gomega v1.20.0 h1:JhAwLmtRzXFTx2AkALSLa8ijZafntmhSoU63Ok18Uq8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -81,121 +34,74 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0= -github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0= +github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= +github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= -github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= -github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY= +github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU= -github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= +github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-redis/redis/v9 v9.0.0-rc.1 h1:/+bS+yeUnanqAbuD3QwlejzQZ+4eqgfUtFTG4b+QnXs= -github.com/go-redis/redis/v9 v9.0.0-rc.1/go.mod h1:8et+z03j0l8N+DvsVnclzjf3Dl/pFHgRk+2Ct1qw66A= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -206,8 +112,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= -github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= +github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -222,58 +128,55 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= +github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= -github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= +github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= -github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= +github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= +github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= -github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 h1:SWlt7BoQNASbhTUD0Oy5yysI2seJ7vWuGUp///OM4TM= github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -285,25 +188,22 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM= -github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc h1:Ak86L+yDSOzKFa7WM5bf5itSOo1e3Xh8bm5YCMUXIjQ= -github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -311,85 +211,69 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/prometheus v2.5.0+incompatible h1:7QPitgO2kOFG8ecuRn9O/4L9+10He72rVRJvMXrE9Hg= github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE= +github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps= github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62/go.mod h1:65XQgovT59RWatovFwnwocoUxiI/eENTnOY5GK3STuY= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +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/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/toolkits/pkg v1.3.1-0.20220824084030-9f9f830a05d5 h1:kMCwr2gNHjHEVgw+uNVdiPbGadj4TekbIfrTXElZeI0= -github.com/toolkits/pkg v1.3.1-0.20220824084030-9f9f830a05d5/go.mod h1:PvTBg/UxazPgBz6VaCM7FM7kJldjfVrsuN6k4HT/VuY= +github.com/toolkits/pkg v1.3.3 h1:qpQAQ18Jr47dv4NcBALlH0ad7L2PuqSh5K+nJKNg5lU= +github.com/toolkits/pkg v1.3.3/go.mod h1:USXArTJlz1f1DCnQHNPYugO8GPkr1NRhP4eYQZQVshk= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -403,342 +287,165 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4 h1:YXPV/eKW0ZWRdB5tyI6aPoaa2Wxb4OSlFrTREMdwn64= -google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M= -gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= -gorm.io/driver/postgres v1.1.1 h1:tWLmqYCyaoh89fi7DhM6QggujrOnmfo3H98AzgNAAu0= -gorm.io/driver/postgres v1.1.1/go.mod h1:tpe2xN7aCst1NUdYyWQyxPtnHC+Zfp6NEux9PXD1OU0= -gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk= -gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.4.4 h1:MX0K9Qvy0Na4o7qSC/YI7XxqUw5KDw01umqgID+svdQ= +gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM= +gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc= +gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0= +gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/docker/n9eetc/alerts/elasticsearch_by_exporter.json b/integrations/elasticsearch/alerts/elasticsearch_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/elasticsearch_by_exporter.json rename to integrations/elasticsearch/alerts/elasticsearch_by_exporter.json diff --git a/integrations/elasticsearch/dashboards/elasticsearch_by_exporter.json b/integrations/elasticsearch/dashboards/elasticsearch_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..d833f6029cf53bde16501567ef22cefd3ffaf3fe --- /dev/null +++ b/integrations/elasticsearch/dashboards/elasticsearch_by_exporter.json @@ -0,0 +1,3274 @@ +{ + "name": "ElasticSearch - 模板", + "tags": "Prometheus ElasticSearch ES", + "ident": "", + "configs": { + "var": [ + { + "name": "cluster", + "definition": "label_values(elasticsearch_indices_docs,cluster)", + "options": [ + "elasticsearch-cluster" + ] + }, + { + "name": "instance", + "definition": "label_values(elasticsearch_indices_docs{cluster=\"$cluster\", name!=\"\"},instance)", + "options": [ + "10.206.0.7:9200" + ] + }, + { + "name": "name", + "definition": "label_values(elasticsearch_indices_docs{instance=\"$instance\",cluster=\"$cluster\", name!=\"\"},name)", + "options": [ + "node-2", + "node-1", + "node-3" + ], + "multi": true, + "allOption": true + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "7d06624d-28be-4586-a804-11ca2f036964", + "type": "row", + "name": "KPI", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "7d06624d-28be-4586-a804-11ca2f036964" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_number_of_nodes{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Data Nodes", + "description": "集群数据节点数量", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 6, + "y": 1, + "i": "ae1ebc12-8639-4812-b5ae-9391a0ec4dbc" + }, + "id": "ae1ebc12-8639-4812-b5ae-9391a0ec4dbc" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_number_of_nodes{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Nodes", + "description": "集群节点数量", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 3, + "y": 1, + "i": "8648c6f9-ab82-4067-a02c-eebe84ae3a96" + }, + "id": "8648c6f9-ab82-4067-a02c-eebe84ae3a96" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum (elasticsearch_process_cpu_percent{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"} ) / count (elasticsearch_process_cpu_percent{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"} )" + } + ], + "name": "CPU usage Avg", + "description": "平均CPU使用率", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "special": 80, + "from": 80 + }, + "result": { + "color": "#f90606" + } + }, + { + "type": "range", + "match": { + "special": 70, + "from": 70 + }, + "result": { + "color": "#f5ac0f" + } + }, + { + "type": "range", + "match": { + "to": 70 + }, + "result": { + "color": "#21c00c" + } + } + ], + "standardOptions": { + "util": "percent" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 9, + "y": 1, + "i": "20bf6dec-36cb-4e3a-a096-275a33e791b9" + }, + "id": "20bf6dec-36cb-4e3a-a096-275a33e791b9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum (elasticsearch_jvm_memory_used_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}) / sum (elasticsearch_jvm_memory_max_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}) * 100" + } + ], + "name": "JVM memory used Avg", + "description": "平均JVM内存使用率", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "special": 80, + "from": 80 + }, + "result": { + "color": "#f12c09" + } + }, + { + "type": "range", + "match": { + "from": 70, + "to": 80 + }, + "result": { + "color": "#fbca18" + } + }, + { + "type": "range", + "match": { + "to": 70 + }, + "result": { + "color": "#21c00c" + } + } + ], + "standardOptions": { + "util": "percent" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 12, + "y": 1, + "i": "6380e6f2-3aec-4d56-a07a-efe6106c492f" + }, + "id": "6380e6f2-3aec-4d56-a07a-efe6106c492f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum (elasticsearch_process_open_files_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"})" + } + ], + "name": "Open file descriptors", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 15, + "y": 1, + "i": "de70ff1e-5ad6-4d68-b961-10eb78840d90" + }, + "id": "de70ff1e-5ad6-4d68-b961-10eb78840d90" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(elasticsearch_breakers_tripped{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"})" + } + ], + "name": "Tripped for breakers", + "description": "集群断路器阻断总数", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "special", + "result": { + "text": "", + "color": "#21c00c" + }, + "match": { + "special": 0 + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 18, + "y": 1, + "i": "88631ba2-b3d4-45e1-966a-9c3eb12a1d49" + }, + "id": "88631ba2-b3d4-45e1-966a-9c3eb12a1d49" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_number_of_pending_tasks{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Pending tasks", + "description": "等待执行的集群变更任务数量", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 5 + }, + "result": { + "color": "#f24207" + } + }, + { + "type": "range", + "match": { + "from": 1, + "to": 5 + }, + "result": { + "color": "#f9a006" + } + }, + { + "type": "range", + "match": { + "to": 1 + }, + "result": { + "color": "#21c00c" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 21, + "y": 1, + "i": "7b85a99f-9cee-4cda-936c-eedcd2409e42" + }, + "id": "7b85a99f-9cee-4cda-936c-eedcd2409e42" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_status{instance=\"$instance\",cluster=\"$cluster\",color=\"red\"}==1 or (elasticsearch_cluster_health_status{instance=\"$instance\",cluster=\"$cluster\",color=\"green\"}==1)+4 or (elasticsearch_cluster_health_status{instance=\"$instance\",cluster=\"$cluster\",color=\"yellow\"}==1)+2" + } + ], + "name": "Cluster health", + "description": "绿色:健康,黄色:存在副本分片异常,红色:存在主分片异常", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "special", + "result": { + "text": "N/A" + }, + "match": {} + }, + { + "type": "special", + "match": { + "special": 5 + }, + "result": { + "text": "Green", + "color": "#21c00c" + } + }, + { + "type": "special", + "match": { + "special": 3 + }, + "result": { + "text": "Yellow", + "color": "#f9e406" + } + }, + { + "type": "special", + "match": { + "special": 1 + }, + "result": { + "text": "Red", + "color": "#f43606" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 0, + "y": 1, + "i": "a9c1c7fa-2985-4a14-bbe9-2ee7db630e94" + }, + "id": "a9c1c7fa-2985-4a14-bbe9-2ee7db630e94" + }, + { + "id": "12024030-3476-44b0-94b1-194629ce4efa", + "type": "row", + "name": "Shards", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "12024030-3476-44b0-94b1-194629ce4efa" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_active_shards{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Active shards", + "description": "活跃分片数\nAggregate total of all shards across all indices, which includes replica shards", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 0, + "y": 5, + "i": "717067e8-5f9c-4485-8158-d977c217d620" + }, + "id": "717067e8-5f9c-4485-8158-d977c217d620" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_active_primary_shards{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Active primary shards", + "description": "活跃主分片数\nThe number of primary shards in your cluster. This is an aggregate total across all indices.", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 4, + "y": 5, + "i": "03e4fbef-93dd-488c-bb52-3eac620cc331" + }, + "id": "03e4fbef-93dd-488c-bb52-3eac620cc331" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_initializing_shards{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Initializing shards", + "description": "创建中分片数量\nCount of shards that are being freshly created", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 8, + "y": 5, + "i": "90b642aa-4815-492b-a821-94a8120f2dc9" + }, + "id": "90b642aa-4815-492b-a821-94a8120f2dc9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_relocating_shards{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Relocating shards", + "description": "迁移中分片数量\nThe number of shards that are currently moving from one node to another node.", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 12, + "y": 5, + "i": "684d1be6-ebdf-4075-a7bc-6580c148b997" + }, + "id": "684d1be6-ebdf-4075-a7bc-6580c148b997" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_unassigned_shards{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Unassigned shards", + "description": "未分配的分片数量\nThe number of shards that exist in the cluster state, but cannot be found in the cluster itself", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 20, + "y": 5, + "i": "df959515-14fa-44e5-a16c-f40f87eacb01" + }, + "id": "df959515-14fa-44e5-a16c-f40f87eacb01" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_cluster_health_delayed_unassigned_shards{instance=\"$instance\",cluster=\"$cluster\"}" + } + ], + "name": "Delayed shards", + "description": "暂缓分配的分片数量,当节点丢失,会发生选主和数据拷贝。为了较少网络抖动等原因导致的重分配情况,配置delay参数,该值为等待delay到期将被重分配的分片数量\nShards delayed to reduce reallocation overhead", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 16, + "y": 5, + "i": "f307f045-cfcf-4cd9-a39f-10d4360a85b2" + }, + "id": "f307f045-cfcf-4cd9-a39f-10d4360a85b2" + }, + { + "id": "ff67d655-1664-4432-bcef-52c00f77ef33", + "type": "row", + "name": "JVM Garbage Collection", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 8, + "i": "ff67d655-1664-4432-bcef-52c00f77ef33" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_jvm_gc_collection_seconds_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}} - {{gc}}" + } + ], + "name": "GC count", + "description": "GC运行次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 9, + "i": "f39502b5-a6a8-4bed-93d7-be83096eecbf" + }, + "id": "f39502b5-a6a8-4bed-93d7-be83096eecbf" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_jvm_gc_collection_seconds_sum{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}} - {{gc}}" + } + ], + "name": "GC time", + "description": "GC运行耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "seconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 9, + "i": "df97e013-e810-4b78-8253-0a1fe7c96b3a" + }, + "id": "df97e013-e810-4b78-8253-0a1fe7c96b3a" + }, + { + "id": "674eb58f-4afe-440b-81eb-2c007c5bf598", + "type": "row", + "name": "Translog", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 16, + "i": "674eb58f-4afe-440b-81eb-2c007c5bf598" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_translog_operations{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Total translog operations", + "description": "translog大小(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 17, + "i": "47583195-3a48-4d76-8b36-c08c29be936e" + }, + "id": "47583195-3a48-4d76-8b36-c08c29be936e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_translog_size_in_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Total translog size in bytes", + "description": "translog大小(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 17, + "i": "dca13d7f-90ca-4302-afed-753c2c181cf5" + }, + "id": "dca13d7f-90ca-4302-afed-753c2c181cf5" + }, + { + "id": "6c7202e2-b991-482b-a6d4-ad26cc97f6d2", + "type": "row", + "name": "Breakers", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 24, + "i": "6c7202e2-b991-482b-a6d4-ad26cc97f6d2" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_breakers_tripped{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}: {{breaker}}" + } + ], + "name": "Tripped for breakers", + "description": "节点断路器阻断总数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 25, + "i": "7040d182-228f-4a58-b4ce-1eb4addeb88f" + }, + "id": "7040d182-228f-4a58-b4ce-1eb4addeb88f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_breakers_estimated_size_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}: {{breaker}}" + }, + { + "expr": "elasticsearch_breakers_limit_size_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "refId": "B", + "legend": "{{name}}: limit for {{breaker}}" + } + ], + "name": "Estimated size in bytes of breaker", + "description": "预估内存大小和限制内存大小", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 25, + "i": "31d1efe8-d58a-4f1c-b510-1854ec1f491a" + }, + "id": "31d1efe8-d58a-4f1c-b510-1854ec1f491a" + }, + { + "id": "0df4ccb6-d6c1-4235-a186-6b596f68dc61", + "type": "row", + "name": "Cpu and Memory", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 32, + "i": "0df4ccb6-d6c1-4235-a186-6b596f68dc61" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_os_load1{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "load1: {{name}}" + }, + { + "expr": "elasticsearch_os_load5{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "refId": "B", + "legend": "load5: {{name}}" + }, + { + "expr": "elasticsearch_os_load15{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "refId": "C", + "legend": "load15: {{name}}" + } + ], + "name": "Load average", + "description": "1m/5m/15m系统负载", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 33, + "i": "1295b795-0e8a-4db7-afc1-5248e386062d" + }, + "id": "1295b795-0e8a-4db7-afc1-5248e386062d" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_process_cpu_percent{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}" + } + ], + "name": "CPU usage", + "description": "进程CPU占比", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 33, + "i": "356d363a-682b-41f9-9fdd-7212e943fc49" + }, + "id": "356d363a-682b-41f9-9fdd-7212e943fc49" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_jvm_memory_used_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}} used: {{area}}" + }, + { + "expr": "elasticsearch_jvm_memory_max_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "refId": "B", + "legend": "{{name}} max: {{area}}" + }, + { + "expr": "elasticsearch_jvm_memory_pool_peak_used_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "refId": "C", + "legend": "{{name}} peak used pool: {{pool}}" + } + ], + "name": "JVM memory usage", + "description": "进程内存占用/限制/峰值(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 35, + "i": "f0216c7e-ade7-47c0-aec1-6efab9543e58" + }, + "id": "f0216c7e-ade7-47c0-aec1-6efab9543e58" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_jvm_memory_committed_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}} committed: {{area}}" + }, + { + "expr": "elasticsearch_jvm_memory_max_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "refId": "B", + "legend": "{{name}} max: {{area}}" + } + ], + "name": "JVM memory committed", + "description": "JVM申请/限制内存(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 35, + "i": "12bce6cc-a0fc-4e98-ba19-5cf5fdeb6332" + }, + "id": "12bce6cc-a0fc-4e98-ba19-5cf5fdeb6332" + }, + { + "id": "df751a4b-72b5-4e4b-bc33-b89f8b4b8cac", + "type": "row", + "name": "Disk and Network", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 42, + "i": "df751a4b-72b5-4e4b-bc33-b89f8b4b8cac" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "1-(elasticsearch_filesystem_data_available_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}/elasticsearch_filesystem_data_size_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"})", + "legend": "{{name}}: {{path}}" + } + ], + "name": "Disk usage", + "description": "磁盘使用率", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percentUnit" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 43, + "i": "5de089e7-c5cd-4a01-9cd7-70e39feef131" + }, + "id": "5de089e7-c5cd-4a01-9cd7-70e39feef131" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_transport_tx_size_bytes_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}: sent" + }, + { + "expr": "-irate(elasticsearch_transport_rx_size_bytes_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "B", + "legend": "{{name}}: received" + } + ], + "name": "Network usage", + "description": "网络流量(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesSI" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 43, + "i": "957402f9-2e17-4c67-8d6b-02a8cc6789d9" + }, + "id": "957402f9-2e17-4c67-8d6b-02a8cc6789d9" + }, + { + "id": "f26bc1c8-5528-4faa-950d-075d39c023a8", + "type": "row", + "name": "Documents", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 50, + "i": "f26bc1c8-5528-4faa-950d-075d39c023a8" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_docs{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}" + } + ], + "name": "Documents count on node", + "description": "节点文档总数,不包含已删除文档和子文档以及刚索引的文档", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 51, + "i": "6220138b-3452-4529-a008-c78e20672169" + }, + "id": "6220138b-3452-4529-a008-c78e20672169" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_indexing_index_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Documents indexed rate", + "description": "平均每秒索引文档数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 51, + "i": "242dff44-09b1-4468-856d-642e76048296" + }, + "id": "242dff44-09b1-4468-856d-642e76048296" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_docs_deleted{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Documents deleted rate", + "description": "平均每秒删除文档数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 53, + "i": "400efbe3-a4c5-49af-a0bf-eabf2511ebf5" + }, + "id": "400efbe3-a4c5-49af-a0bf-eabf2511ebf5" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(elasticsearch_indices_merges_docs_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Documents merged rate", + "description": "平均每秒合并文档数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 53, + "i": "0b2d6480-6f50-413f-8552-f49109d2be59" + }, + "id": "0b2d6480-6f50-413f-8552-f49109d2be59" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_merges_total_size_bytes_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Documents merged bytes", + "description": "平均每秒合并文档大小", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesSI" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 53, + "i": "981c2d24-56df-437c-bc43-77682008dd01" + }, + "id": "981c2d24-56df-437c-bc43-77682008dd01" + }, + { + "id": "301ccb76-3b75-48a3-b37c-0ab035c2c2e9", + "type": "row", + "name": "Times", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 60, + "i": "301ccb76-3b75-48a3-b37c-0ab035c2c2e9" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_search_query_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Query time", + "description": "查询操作耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "seconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 61, + "i": "e217b136-6021-473a-859e-7ac4218c4e86" + }, + "id": "e217b136-6021-473a-859e-7ac4218c4e86" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_indexing_index_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Indexing time", + "description": "索引操作耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "seconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 61, + "i": "d51a4ef1-1fc5-4d1b-ba6c-a2fd483295ee" + }, + "id": "d51a4ef1-1fc5-4d1b-ba6c-a2fd483295ee" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_merges_total_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Merging time", + "description": "合并操作耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "seconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 63, + "i": "20751b64-38da-4263-a055-02bc844bd416" + }, + "id": "20751b64-38da-4263-a055-02bc844bd416" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_store_throttle_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Throttle time for index store", + "description": "索引存储限制耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "seconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 63, + "i": "d688b715-5959-433a-89b2-51bcd2b0b1f3" + }, + "id": "d688b715-5959-433a-89b2-51bcd2b0b1f3" + }, + { + "id": "f83e7128-21d7-4d5d-9956-61c9537f9b20", + "type": "row", + "name": "Total Operations states", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 70, + "i": "f83e7128-21d7-4d5d-9956-61c9537f9b20" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_indices_indexing_index_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}: indexing" + }, + { + "expr": "irate(elasticsearch_indices_search_query_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "B", + "legend": "{{name}}: query" + }, + { + "expr": "irate(elasticsearch_indices_search_fetch_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "C", + "legend": "{{name}}: fetch" + }, + { + "expr": "irate(elasticsearch_indices_merges_total_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "D", + "legend": "{{name}}: merges" + }, + { + "expr": "irate(elasticsearch_indices_refresh_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "E", + "legend": "{{name}}: refresh" + }, + { + "expr": "irate(elasticsearch_indices_flush_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "F", + "legend": "{{name}}: flush" + }, + { + "expr": "irate(elasticsearch_indices_get_exists_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "G", + "legend": "{{name}}: get_exists" + }, + { + "expr": "irate(elasticsearch_indices_get_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "H", + "legend": "{{name}}: get_time" + }, + { + "expr": "irate(elasticsearch_indices_get_missing_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "I", + "legend": "{{name}}: get_missing" + }, + { + "expr": "irate(elasticsearch_indices_indexing_delete_time_seconds_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "K", + "legend": "{{name}}: indexing_delete" + }, + { + "expr": "irate(elasticsearch_indices_get_time_seconds{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "L", + "legend": "{{name}}: get" + } + ], + "name": "Total Operations time", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "seconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 71, + "i": "9be3061f-ade2-43a5-ab5f-bfdc8861e4be" + }, + "id": "9be3061f-ade2-43a5-ab5f-bfdc8861e4be" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(elasticsearch_indices_indexing_index_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}: indexing" + }, + { + "expr": "rate(elasticsearch_indices_search_query_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "B", + "legend": "{{name}}: query" + }, + { + "expr": "rate(elasticsearch_indices_search_fetch_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "C", + "legend": "{{name}}: fetch" + }, + { + "expr": "rate(elasticsearch_indices_merges_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "D", + "legend": "{{name}}: merges" + }, + { + "expr": "rate(elasticsearch_indices_refresh_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "E", + "legend": "{{name}}: refresh" + }, + { + "expr": "rate(elasticsearch_indices_flush_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "F", + "legend": "{{name}}: flush" + }, + { + "expr": "rate(elasticsearch_indices_get_exists_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "G", + "legend": "{{name}}: get_exists" + }, + { + "expr": "rate(elasticsearch_indices_get_missing_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "H", + "legend": "{{name}}: get_missing" + }, + { + "expr": "rate(elasticsearch_indices_get_tota{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "I", + "legend": "{{name}}: get" + }, + { + "expr": "rate(elasticsearch_indices_indexing_delete_total{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "refId": "K", + "legend": "{{name}}: indexing_delete" + } + ], + "name": "Total Operations rate", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 71, + "i": "55cf0c8e-bf4f-430c-bc68-1eca72d7055d" + }, + "id": "55cf0c8e-bf4f-430c-bc68-1eca72d7055d" + }, + { + "id": "7f948b8c-2881-416c-83b7-0819823c7b70", + "type": "row", + "name": "Thread Pool", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 78, + "i": "7f948b8c-2881-416c-83b7-0819823c7b70" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_thread_pool_rejected_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}: {{ type }}" + } + ], + "name": "Thread Pool operations rejected", + "description": "线程池reject次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 79, + "i": "d337f12c-6fbe-4463-9181-faecdc612a90" + }, + "id": "d337f12c-6fbe-4463-9181-faecdc612a90" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_thread_pool_active_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}: {{ type }}" + } + ], + "name": "Thread Pool threads active", + "description": "活跃线程数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 79, + "i": "1053fab0-f105-4a6b-9508-dc87c4f78956" + }, + "id": "1053fab0-f105-4a6b-9508-dc87c4f78956" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_thread_pool_queue_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}: {{ type }}" + } + ], + "name": "Thread Pool threads queued", + "description": "排队等待线程任务数量", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 79, + "i": "aad51280-5725-4485-841d-c08f0277508f" + }, + "id": "aad51280-5725-4485-841d-c08f0277508f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "irate(elasticsearch_thread_pool_completed_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}: {{ type }}" + } + ], + "name": "Thread Pool operations completed", + "description": "线程池complete次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 79, + "i": "76538cda-67f3-4040-b42e-be27078440e4" + }, + "id": "76538cda-67f3-4040-b42e-be27078440e4" + }, + { + "id": "2f6f4c26-2203-4f94-a105-a166f35608da", + "type": "row", + "name": "Caches", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 86, + "i": "2f6f4c26-2203-4f94-a105-a166f35608da" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_fielddata_memory_size_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}" + } + ], + "name": "Field data memory size", + "description": "fielddata cache内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 87, + "i": "100f8d6d-073b-46f4-a664-db11e889a111" + }, + "id": "100f8d6d-073b-46f4-a664-db11e889a111" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(elasticsearch_indices_fielddata_evictions{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Field data evictions", + "description": "fielddata cache平均每秒内存剔除次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 87, + "i": "4af09644-306e-404d-a1a0-174397b26c4e" + }, + "id": "4af09644-306e-404d-a1a0-174397b26c4e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_query_cache_memory_size_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}" + } + ], + "name": "Query cache size", + "description": "query cache内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 89, + "i": "e9632f58-8186-43bf-81b1-c57e91078e35" + }, + "id": "e9632f58-8186-43bf-81b1-c57e91078e35" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(elasticsearch_indices_query_cache_evictions{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Query cache evictions", + "description": "query cache平均每秒内存剔除次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 89, + "i": "e1145b0d-b839-4189-bd02-d955ae3dbcf3" + }, + "id": "e1145b0d-b839-4189-bd02-d955ae3dbcf3" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(elasticsearch_indices_filter_cache_evictions{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}[5m])", + "legend": "{{name}}" + } + ], + "name": "Evictions from filter cache", + "description": "老版本的filter cache内存剔除次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 89, + "i": "14ce9ebf-5ff7-42da-9628-8746e18fe32b" + }, + "id": "14ce9ebf-5ff7-42da-9628-8746e18fe32b" + }, + { + "id": "f645ba3f-d2eb-4335-a2ff-56037184c779", + "type": "row", + "name": "Segments", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 96, + "i": "f645ba3f-d2eb-4335-a2ff-56037184c779" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segments_count{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}" + } + ], + "name": "Count of index segments", + "description": "segment个数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 97, + "i": "7f3f74ca-a8ca-4d07-b17a-72bb198f9726" + }, + "id": "7f3f74ca-a8ca-4d07-b17a-72bb198f9726" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segments_memory_bytes{instance=\"$instance\",cluster=\"$cluster\",name=~\"$name\"}", + "legend": "{{name}}" + } + ], + "name": "Current memory size of segments in bytes", + "description": "segment内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 97, + "i": "0d2bbc28-2211-4097-acbc-e1f72b3cad88" + }, + "id": "0d2bbc28-2211-4097-acbc-e1f72b3cad88" + }, + { + "id": "6e28b8b4-dc24-45fb-9d60-351f08716296", + "type": "row", + "name": "Indices: Count of documents and Total size", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 104, + "i": "6e28b8b4-dc24-45fb-9d60-351f08716296" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_docs_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Count of documents with only primary shards", + "description": "主分片文档数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "stack": "off", + "lineInterpolation": "smooth", + "fillOpacity": 0.5 + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 105, + "i": "8620f336-4e26-40db-8c85-6a336a5fb7c9" + }, + "id": "8620f336-4e26-40db-8c85-6a336a5fb7c9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_store_size_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Total size of stored index data in bytes with only primary shards on all nodes", + "description": "主分片索引容量(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 107, + "i": "d4541f1a-136f-4b3e-b5e9-8cc92267aa8a" + }, + "id": "d4541f1a-136f-4b3e-b5e9-8cc92267aa8a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_store_size_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Total size of stored index data in bytes with all shards on all nodes", + "description": "所有分片索引容量(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 109, + "i": "66926f5a-d562-428e-b48b-d69b4d705b8f" + }, + "id": "66926f5a-d562-428e-b48b-d69b4d705b8f" + }, + { + "id": "c92992cf-3b56-4472-9278-9103b8fe55b7", + "type": "row", + "name": "Indices: Index writer", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 116, + "i": "c92992cf-3b56-4472-9278-9103b8fe55b7" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_index_writer_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Index writer with only primary shards on all nodes in bytes", + "description": "主分片索引写入数据量(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 117, + "i": "7837ae60-c490-4e8a-a604-133cdeb5a3e7" + }, + "id": "7837ae60-c490-4e8a-a604-133cdeb5a3e7" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_index_writer_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Index writer with all shards on all nodes in bytes", + "description": "所有分片索引写入数据量(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 119, + "i": "8f200ba5-8c9b-4a26-b7b3-307cdb504316" + }, + "id": "8f200ba5-8c9b-4a26-b7b3-307cdb504316" + }, + { + "id": "fb8887c4-c0a4-44c1-83da-f077fa0feb9d", + "type": "row", + "name": "Indices: Segments", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 126, + "i": "fb8887c4-c0a4-44c1-83da-f077fa0feb9d" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_count_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Segments with only primary shards on all nodes", + "description": "主分片segment数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 127, + "i": "d20fb2f9-0e51-4bd1-a580-1308349e0b71" + }, + "id": "d20fb2f9-0e51-4bd1-a580-1308349e0b71" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_count_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Segments with all shards on all nodes", + "description": "所有分片segment总数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 129, + "i": "b8f5151d-d7d9-4cf2-822e-56ce5b0f58b2" + }, + "id": "b8f5151d-d7d9-4cf2-822e-56ce5b0f58b2" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of segments with only primary shards on all nodes in bytes", + "description": "主分片segment容量", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 131, + "i": "c6c1eee0-4c93-462e-8c53-f369fee48e46" + }, + "id": "c6c1eee0-4c93-462e-8c53-f369fee48e46" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of segments with all shards on all nodes in bytes", + "description": "所有分片segment容量", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 133, + "i": "d9b36d80-c753-4bde-8e6d-29af8c5a7e83" + }, + "id": "d9b36d80-c753-4bde-8e6d-29af8c5a7e83" + }, + { + "id": "abf4df7c-975a-493e-8951-be33c7d3ec6b", + "type": "row", + "name": "Indices: Doc values", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 140, + "i": "abf4df7c-975a-493e-8951-be33c7d3ec6b" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_doc_values_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Doc values with only primary shards on all nodes in bytes", + "description": "主分片doc value内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 141, + "i": "205f362a-ad57-4cb8-ac3f-7afdb5dc87d6" + }, + "id": "205f362a-ad57-4cb8-ac3f-7afdb5dc87d6" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_doc_values_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Doc values with all shards on all nodes in bytes", + "description": "所有分片doc value内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 143, + "i": "ea838448-825c-4627-a09d-19db6c1e4a14" + }, + "id": "ea838448-825c-4627-a09d-19db6c1e4a14" + }, + { + "id": "43768cea-7ee8-430a-9aa3-e8c38817de55", + "type": "row", + "name": "Indices: Fields", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 150, + "i": "43768cea-7ee8-430a-9aa3-e8c38817de55" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_fields_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of fields with only primary shards on all nodes in bytes", + "description": "分片field内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 151, + "i": "6f8efb01-aac1-427c-bf7b-d6b90a968185" + }, + "id": "6f8efb01-aac1-427c-bf7b-d6b90a968185" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_fields_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of fields with all shards on all nodes in bytes", + "description": "所有分片field内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 153, + "i": "b82b390b-8f72-427b-9d1f-37d279f94366" + }, + "id": "b82b390b-8f72-427b-9d1f-37d279f94366" + }, + { + "id": "41285e05-696c-4a94-946f-f75021316665", + "type": "row", + "name": "Indices: Fixed bit", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 160, + "i": "41285e05-696c-4a94-946f-f75021316665" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_fixed_bit_set_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of fixed bit with only primary shards on all nodes in bytes", + "description": "主分片fixed bit set内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 161, + "i": "707cfc5f-cdbe-43dc-9d1f-f79e306ca7ff" + }, + "id": "707cfc5f-cdbe-43dc-9d1f-f79e306ca7ff" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_fixed_bit_set_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of fixed bit with all shards on all nodes in bytes", + "description": "所有分片fixed bit set内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 163, + "i": "ee949fc8-2c50-4ca3-944f-b42030439968" + }, + "id": "ee949fc8-2c50-4ca3-944f-b42030439968" + }, + { + "id": "23141b77-a525-44b1-a3b8-9ff4a65af597", + "type": "row", + "name": "Indices: Norms", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 170, + "i": "23141b77-a525-44b1-a3b8-9ff4a65af597" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_norms_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of norms with only primary shards on all nodes in bytes", + "description": "主分片normalization factor内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 171, + "i": "167f10ca-179d-4dfd-ba1d-e2184be993be" + }, + "id": "167f10ca-179d-4dfd-ba1d-e2184be993be" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_norms_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of norms with all shards on all nodes in bytes", + "description": "所有分片normalization factor内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 173, + "i": "41fba22d-bbb0-4cbd-a8cd-c7910991cecf" + }, + "id": "41fba22d-bbb0-4cbd-a8cd-c7910991cecf" + }, + { + "id": "1f40b15b-8f0e-4c57-a1fd-602242195863", + "type": "row", + "name": "Indices: Points", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 180, + "i": "1f40b15b-8f0e-4c57-a1fd-602242195863" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_points_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of points with only primary shards on all nodes in bytes", + "description": "主分片point内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 181, + "i": "24e451cb-3835-4172-926b-54a33aa3b9b4" + }, + "id": "24e451cb-3835-4172-926b-54a33aa3b9b4" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_points_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of points with all shards on all nodes in bytes", + "description": "所有分片point内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 183, + "i": "79665ebe-5804-4809-af68-15d3a72a2c2c" + }, + "id": "79665ebe-5804-4809-af68-15d3a72a2c2c" + }, + { + "id": "a62b3a9f-f3ac-4c4e-9d20-bde6c9eb3372", + "type": "row", + "name": "Indices: Terms", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 190, + "i": "a62b3a9f-f3ac-4c4e-9d20-bde6c9eb3372" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_terms_memory_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of terms with only primary shards on all nodes in bytes", + "description": "主分片term内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 191, + "i": "c464e467-a6cf-4d60-8108-fb0359c801d8" + }, + "id": "c464e467-a6cf-4d60-8108-fb0359c801d8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_terms_memory_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Number of terms with all shards on all nodes in bytes", + "description": "所有分片term内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 193, + "i": "b0befc26-33c8-44f3-aa3e-80a9ed99180a" + }, + "id": "b0befc26-33c8-44f3-aa3e-80a9ed99180a" + }, + { + "id": "df60be90-b370-4995-93c6-b26ee8584e6f", + "type": "row", + "name": "Indices: Terms", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 200, + "i": "df60be90-b370-4995-93c6-b26ee8584e6f" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_version_map_memory_bytes_primary{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of version map with only primary shards on all nodes in bytes", + "description": "所有分片version map内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 201, + "i": "a2778c3a-6473-4482-bade-398904922e2f" + }, + "id": "a2778c3a-6473-4482-bade-398904922e2f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "elasticsearch_indices_segment_version_map_memory_bytes_total{instance=~\"$instance\"}", + "legend": "{{index}}" + } + ], + "name": "Size of version map with all shards on all nodes in bytes", + "description": "所有分片version map内存占用(byte)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 203, + "i": "322b0c1b-982a-4654-8552-7f246f496480" + }, + "id": "322b0c1b-982a-4654-8552-7f246f496480" + } + ] + } +} \ No newline at end of file diff --git a/integrations/elasticsearch/icon/es.svg b/integrations/elasticsearch/icon/es.svg new file mode 100644 index 0000000000000000000000000000000000000000..4de74c51d1a4916739c07f8482a5f76b9b4c045c --- /dev/null +++ b/integrations/elasticsearch/icon/es.svg @@ -0,0 +1,28 @@ + + + icon / product-logo / 32x32px / elasticsearch / color + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docker/n9eetc/alerts/http_response_by_categraf.json b/integrations/http/alerts/http_response_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/http_response_by_categraf.json rename to integrations/http/alerts/http_response_by_categraf.json diff --git a/integrations/http/dashboards/http_response_by_categraf.json b/integrations/http/dashboards/http_response_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..1a1306f2b6b674bc850d192b49d81c9ae2bfd436 --- /dev/null +++ b/integrations/http/dashboards/http_response_by_categraf.json @@ -0,0 +1,112 @@ +{ + "name": "HTTP探测", + "tags": "", + "ident": "", + "configs": { + "version": "2.0.0", + "panels": [ + { + "id": "0cd7c8aa-456c-4522-97ef-0b1710e7af8a", + "type": "row", + "name": "Default chart group", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "0cd7c8aa-456c-4522-97ef-0b1710e7af8a" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "max(http_response_result_code) by (target)", + "legend": "UP?" + }, + { + "expr": "max(http_response_response_code) by (target)", + "refId": "B", + "legend": "status code" + }, + { + "expr": "max(http_response_response_time) by (target)", + "refId": "C", + "legend": "latency(s)" + }, + { + "expr": "max(http_response_cert_expire_timestamp) by (target) - time()", + "refId": "D", + "legend": "cert expire" + } + ], + "name": "URL Details", + "custom": { + "showHeader": true, + "calc": "lastNotNull", + "displayMode": "labelValuesToRows", + "aggrDimension": "target" + }, + "options": { + "valueMappings": [], + "standardOptions": {} + }, + "overrides": [ + { + "properties": { + "valueMappings": [ + { + "type": "special", + "match": { + "special": 0 + }, + "result": { + "text": "UP", + "color": "#417505" + } + }, + { + "type": "range", + "match": { + "special": 1, + "from": 1 + }, + "result": { + "text": "DOWN", + "color": "#e90f0f" + } + } + ], + "standardOptions": {} + }, + "matcher": { + "value": "A" + } + }, + { + "type": "special", + "matcher": { + "value": "D" + }, + "properties": { + "standardOptions": { + "util": "humantimeSeconds" + } + } + } + ], + "version": "2.0.0", + "type": "table", + "layout": { + "h": 15, + "w": 24, + "x": 0, + "y": 1, + "i": "3674dbfa-243a-49f6-baa5-b7f887c1afb0" + }, + "id": "3674dbfa-243a-49f6-baa5-b7f887c1afb0" + } + ] + } +} \ No newline at end of file diff --git a/integrations/http/icon/http.png b/integrations/http/icon/http.png new file mode 100644 index 0000000000000000000000000000000000000000..500fdec41501403d37b737d8e5088d23edd6fcb6 Binary files /dev/null and b/integrations/http/icon/http.png differ diff --git a/integrations/jmx/dashboards/jmx_by_exporter.json b/integrations/jmx/dashboards/jmx_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..62a58b40bd50b0a403f85693574288a573c97b4f --- /dev/null +++ b/integrations/jmx/dashboards/jmx_by_exporter.json @@ -0,0 +1,976 @@ +{ + "name": "JMX - 模板", + "tags": "Prometheus JMX", + "ident": "", + "configs": { + "var": [ + { + "name": "job", + "definition": "jmx_exporter" + }, + { + "name": "instance", + "definition": "label_values(jmx_scrape_error,instance)" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "a26c5c3d-7b60-4746-bd1f-ca95581cf2fd", + "type": "row", + "name": "Basic Info", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "a26c5c3d-7b60-4746-bd1f-ca95581cf2fd" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "up{job=\"$job\", instance=\"$instance\"}" + } + ], + "name": "Status", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "special", + "match": { + "special": 1 + }, + "result": { + "text": "UP", + "color": "#1eac02" + } + }, + { + "type": "special", + "match": { + "special": 0 + }, + "result": { + "text": "DOWN", + "color": "#f00a0a" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "0721ee76-816b-469f-9c49-2bef94a9299e" + }, + "id": "0721ee76-816b-469f-9c49-2bef94a9299e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "time() - process_start_time_seconds{job=\"$job\",instance=\"$instance\"}" + } + ], + "name": "Uptime", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "humantimeSeconds" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "a55c40fc-dc25-4d2a-8e99-928e02c5ff5d" + }, + "id": "a55c40fc-dc25-4d2a-8e99-928e02c5ff5d" + }, + { + "targets": [ + { + "refId": "A", + "expr": "os_available_processors{job=\"$job\",instance=\"$instance\"}" + } + ], + "name": "Available CPUs", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "60c3389c-808d-4412-b74b-cb762e89a8ad" + }, + "id": "60c3389c-808d-4412-b74b-cb762e89a8ad" + }, + { + "targets": [ + { + "refId": "A", + "expr": "os_open_file_descriptor_count{job=\"$job\",instance=\"$instance\"}" + } + ], + "name": "Open file descriptors", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "1c9a8cca-3578-485e-837d-21618d383065" + }, + "id": "1c9a8cca-3578-485e-837d-21618d383065" + }, + { + "id": "705c90e0-e8b6-4f1c-b35c-c8a785009a20", + "type": "row", + "name": "JVM Memory", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "705c90e0-e8b6-4f1c-b35c-c8a785009a20" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_bytes_used{area=\"heap\",job=\"$job\",instance=\"$instance\"}", + "legend": "Used" + }, + { + "expr": "jvm_memory_bytes_max{area=\"heap\",job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Max" + } + ], + "name": "JVM Memory(heap)", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 5, + "i": "5455e2f2-f6bb-4888-9d88-240d7e12cce2" + }, + "id": "5455e2f2-f6bb-4888-9d88-240d7e12cce2" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_bytes_used{area=\"nonheap\",job=\"$job\",instance=\"$instance\"}", + "legend": "Used" + }, + { + "expr": "jvm_memory_bytes_max{area=\"nonheap\",job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Max" + } + ], + "name": "JVM Memory(nonheap)", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 5, + "i": "765b22a9-1ddc-4c08-8758-684e3c13252b" + }, + "id": "765b22a9-1ddc-4c08-8758-684e3c13252b" + }, + { + "id": "c43aa6f5-7252-400f-bb9f-8c96e436151c", + "type": "row", + "name": "Memory Pool", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "c43aa6f5-7252-400f-bb9f-8c96e436151c" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"CodeHeap 'non-nmethods'\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"CodeHeap 'non-nmethods'\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"CodeHeap 'non-nmethods'\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "CodeHeap 'non-nmethods'", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 13, + "i": "5ab2434c-a905-43c1-a563-4cee2dc9dce9" + }, + "id": "5ab2434c-a905-43c1-a563-4cee2dc9dce9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"CodeHeap 'profiled nmethods'\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"CodeHeap 'profiled nmethods'\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"CodeHeap 'profiled nmethods'\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "CodeHeap 'profiled nmethods'", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 13, + "i": "bfe16d07-91ff-44e6-87bc-9d5d93d2ebd6" + }, + "id": "bfe16d07-91ff-44e6-87bc-9d5d93d2ebd6" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"CodeHeap 'non-profiled nmethods'\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"CodeHeap 'non-profiled nmethods'\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"CodeHeap 'non-profiled nmethods'\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "CodeHeap 'non-profiled nmethods'", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 13, + "i": "18d10f97-5ab2-41c4-a3ad-09f2c7a03e1a" + }, + "id": "18d10f97-5ab2-41c4-a3ad-09f2c7a03e1a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"G1 Eden Space\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"G1 Eden Space\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"G1 Eden Space\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "G1 Eden Space", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 13, + "i": "314a3893-c1d4-4f85-bce0-33ecfda2f521" + }, + "id": "314a3893-c1d4-4f85-bce0-33ecfda2f521" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"Compressed Class Space\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"Compressed Class Space\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"Compressed Class Space\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "Compressed Class Space", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 15, + "i": "1e5f03e7-af5d-447b-9c1b-23d81915e8df" + }, + "id": "1e5f03e7-af5d-447b-9c1b-23d81915e8df" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"G1 Survivor Space\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"G1 Survivor Space\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"G1 Survivor Space\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "G1 Survivor Space", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 15, + "i": "86a68ff6-238c-4fc9-b77e-3b964e564500" + }, + "id": "86a68ff6-238c-4fc9-b77e-3b964e564500" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"G1 Old Gen\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"G1 Old Gen\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"G1 Old Gen\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "G1 Old Gen", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 15, + "i": "595af7d1-e53c-43b5-8f62-ddb9b3a4ffcb" + }, + "id": "595af7d1-e53c-43b5-8f62-ddb9b3a4ffcb" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_memory_pool_bytes_max{pool=\"Metaspace\", job=\"$job\",instance=\"$instance\"}", + "legend": "Max" + }, + { + "expr": "jvm_memory_pool_bytes_used{pool=\"Metaspace\", job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Used" + }, + { + "expr": "jvm_memory_pool_bytes_committed{pool=\"Metaspace\", job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Committed" + } + ], + "name": "Metaspace", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 15, + "i": "380fdfcb-16a6-4131-abaa-a3911b7de6fa" + }, + "id": "380fdfcb-16a6-4131-abaa-a3911b7de6fa" + }, + { + "id": "0aaf3516-4938-41e3-b7cb-323de6de75d9", + "type": "row", + "name": "GC", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 22, + "i": "0aaf3516-4938-41e3-b7cb-323de6de75d9" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "increase(jvm_gc_collection_seconds_sum{job=\"$job\",instance=~\"$instance\"}[1m])" + } + ], + "name": "过去一分钟GC耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 23, + "i": "5303bda0-47c2-4aca-bb12-1da512500f4a" + }, + "id": "5303bda0-47c2-4aca-bb12-1da512500f4a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "increase(jvm_gc_collection_seconds_count{job=\"$job\",instance=\"$instance\"}[1m])", + "legend": "" + } + ], + "name": "过去一分钟GC次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 23, + "i": "cf410459-b5df-4aca-a410-ecda091d6097" + }, + "id": "cf410459-b5df-4aca-a410-ecda091d6097" + }, + { + "targets": [ + { + "refId": "A", + "expr": "increase(jvm_gc_collection_seconds_sum{job=\"$job\",instance=\"$instance\"}[1m])/increase(jvm_gc_collection_seconds_count{job=\"$job\",instance=\"$instance\"}[1m])", + "legend": "" + } + ], + "name": "过去一分钟每次GC平均耗时(秒)", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "none" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "bars", + "stack": "off", + "fillOpacity": 1 + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 23, + "i": "30feb928-b7c3-4e71-aeeb-cc10994b313c" + }, + "id": "30feb928-b7c3-4e71-aeeb-cc10994b313c" + }, + { + "id": "fd6d0772-40d7-4211-b9bb-601e35fb6431", + "type": "row", + "name": "Threads and Class loading", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 30, + "i": "fd6d0772-40d7-4211-b9bb-601e35fb6431" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_threads_current{job=\"$job\",instance=\"$instance\"}", + "legend": "current" + }, + { + "expr": "jvm_threads_daemon{job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "daemon" + }, + { + "expr": "jvm_threads_deadlocked{job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "deadlocked" + } + ], + "name": "Threads", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 31, + "i": "65c74a2b-5f01-4491-b45a-dffe4a9b678a" + }, + "id": "65c74a2b-5f01-4491-b45a-dffe4a9b678a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "jvm_classes_loaded{job=\"$job\", instance=\"$instance\"}" + } + ], + "name": "Class loading", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 31, + "i": "2da16907-adf7-4561-9338-4254c89a311b" + }, + "id": "2da16907-adf7-4561-9338-4254c89a311b" + }, + { + "id": "12fe119e-54f0-4219-9846-ac982c1e9b4d", + "type": "row", + "name": "Physical memory", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 38, + "i": "12fe119e-54f0-4219-9846-ac982c1e9b4d" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "os_total_physical_memory_bytes{job=\"$job\",instance=\"$instance\"}", + "legend": "Total physical memory" + }, + { + "expr": "os_committed_virtual_memory_bytes{job=\"$job\",instance=\"$instance\"}", + "refId": "B", + "legend": "Committed virtual memory" + }, + { + "expr": "os_free_physical_memory_bytes{job=\"$job\",instance=\"$instance\"}", + "refId": "C", + "legend": "Free physical memory" + } + ], + "name": "Physical memory", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 39, + "i": "5a859147-edfc-4dac-9457-8a928213bc00" + }, + "id": "5a859147-edfc-4dac-9457-8a928213bc00" + } + ] + } +} \ No newline at end of file diff --git a/integrations/jmx/icon/java.png b/integrations/jmx/icon/java.png new file mode 100644 index 0000000000000000000000000000000000000000..0a7aa50593f369f415462776b1f351fa42057c0b Binary files /dev/null and b/integrations/jmx/icon/java.png differ diff --git a/etc/alerts/kafka_by_exporter.json b/integrations/kafka/alerts/kafka_by_exporter.json similarity index 100% rename from etc/alerts/kafka_by_exporter.json rename to integrations/kafka/alerts/kafka_by_exporter.json diff --git a/etc/dashboards/kafka_by_exporter.json b/integrations/kafka/dashboards/kafka_by_exporter.json similarity index 69% rename from etc/dashboards/kafka_by_exporter.json rename to integrations/kafka/dashboards/kafka_by_exporter.json index b2666468d39df7d65678e49f253c1017d9b4aae6..d048fbddf4be23dd35270201afde532a87432fe3 100644 --- a/etc/dashboards/kafka_by_exporter.json +++ b/integrations/kafka/dashboards/kafka_by_exporter.json @@ -1,18 +1,22 @@ { "name": "Kafka - 模板", - "tags": "Kafka Prometheus", + "tags": "Kafka Prometheus ", + "ident": "", "configs": { "var": [ { - "name": "cluster", - "definition": "label_values(kafka_brokers, cluster)", - "type": "query" + "name": "instance", + "definition": "label_values(kafka_brokers, instance)" + }, + { + "name": "job", + "definition": "label_values(kafka_brokers, job)" } ], "version": "2.0.0", "panels": [ { - "id": "51502c3a-dd6f-41c7-b8f1-87b88826c96e", + "id": "a3ac9979-6e3a-42ae-9d52-ebddb8960dc4", "type": "row", "name": "overview", "layout": { @@ -20,8 +24,7 @@ "w": 24, "x": 0, "y": 0, - "i": "51502c3a-dd6f-41c7-b8f1-87b88826c96e", - "isResizable": false + "i": "a3ac9979-6e3a-42ae-9d52-ebddb8960dc4" }, "collapsed": true }, @@ -29,10 +32,10 @@ "targets": [ { "refId": "A", - "expr": "kafka_brokers{cluster=\"$cluster\"}" + "expr": "count(count by (topic) (kafka_topic_partitions))" } ], - "name": "brokers", + "name": "topics", "custom": { "textMode": "value", "colorMode": "value", @@ -49,22 +52,21 @@ "type": "stat", "layout": { "h": 3, - "w": 6, - "x": 0, + "w": 8, + "x": 8, "y": 1, - "i": "e2c1d271-ec43-4821-aa19-451e856af755", - "isResizable": true + "i": "ed68dc7b-4f01-4aef-ab10-20158aadfab7" }, - "id": "e2c1d271-ec43-4821-aa19-451e856af755" + "id": "ed68dc7b-4f01-4aef-ab10-20158aadfab7" }, { "targets": [ { "refId": "A", - "expr": "count(count by (topic) (kafka_topic_partitions{cluster=\"$cluster\"}))" + "expr": "kafka_brokers" } ], - "name": "topics", + "name": "brokers", "custom": { "textMode": "value", "colorMode": "value", @@ -81,19 +83,18 @@ "type": "stat", "layout": { "h": 3, - "w": 6, - "x": 6, + "w": 8, + "x": 0, "y": 1, - "i": "fd3a0b9f-fd67-4360-a94c-869fee7b5b98", - "isResizable": true + "i": "3678c9d7-cb0a-4114-a0cd-7a06b976f6b8" }, - "id": "fd3a0b9f-fd67-4360-a94c-869fee7b5b98" + "id": "3678c9d7-cb0a-4114-a0cd-7a06b976f6b8" }, { "targets": [ { "refId": "A", - "expr": "sum(kafka_topic_partitions{cluster=\"$cluster\"})" + "expr": "sum(kafka_topic_partitions)" } ], "name": "partitions", @@ -113,46 +114,15 @@ "type": "stat", "layout": { "h": 3, - "w": 6, - "x": 12, - "y": 1, - "i": "e228d857-746b-41b6-8d2d-0152453c46f4", - "isResizable": true - }, - "id": "e228d857-746b-41b6-8d2d-0152453c46f4" - }, - { - "targets": [ - { - "refId": "A", - "expr": "sum(kafka_topic_partition_replicas{cluster=\"$cluster\"})" - } - ], - "name": "Replicas", - "custom": { - "textMode": "valueAndName", - "colorMode": "value", - "calc": "lastNotNull", - "colSpan": 1, - "textSize": {} - }, - "options": { - "standardOptions": {} - }, - "version": "2.0.0", - "type": "stat", - "layout": { - "h": 3, - "w": 6, - "x": 18, + "w": 8, + "x": 16, "y": 1, - "i": "85438099-8d6b-4817-b9b9-1d0ed36029cd", - "isResizable": true + "i": "8adb0df0-13bc-452a-ac63-209ae3748d77" }, - "id": "85438099-8d6b-4817-b9b9-1d0ed36029cd" + "id": "8adb0df0-13bc-452a-ac63-209ae3748d77" }, { - "id": "0db4aac4-86cf-44cd-950e-6c6a99be8ff4", + "id": "7071dc1f-9410-4899-9c43-206a11bfaab2", "type": "row", "name": "throughput", "layout": { @@ -160,18 +130,17 @@ "w": 24, "x": 0, "y": 4, - "i": "0db4aac4-86cf-44cd-950e-6c6a99be8ff4", - "isResizable": false + "i": "7071dc1f-9410-4899-9c43-206a11bfaab2" }, "collapsed": true }, { "targets": [ { - "expr": "sum(rate(kafka_topic_partition_current_offset{cluster=\"$cluster\"}[1m])) by (topic)" + "expr": "sum(rate(kafka_topic_partition_current_offset{instance=\"$instance\"}[1m])) by (topic)" } ], - "name": "Messages produced per second", + "name": "Message in per second", "options": { "tooltip": { "mode": "all", @@ -193,21 +162,58 @@ "type": "timeseries", "layout": { "h": 7, - "w": 8, + "w": 12, "x": 0, "y": 5, - "i": "c2ec4036-3081-45cc-b672-024c6df93833", - "isResizable": true + "i": "b68719ad-ba54-4326-a956-43acaef10e2e" }, - "id": "c2ec4036-3081-45cc-b672-024c6df93833" + "id": "b68719ad-ba54-4326-a956-43acaef10e2e" }, { "targets": [ { - "expr": "sum(rate(kafka_consumergroup_current_offset{cluster=\"$cluster\"}[1m])) by (topic)" + "expr": "sum(kafka_consumer_lag_millis{instance=\"$instance\"}) by (consumergroup, topic) ", + "legend": "{{consumergroup}} (topic: {{topic}})" } ], - "name": "Messages consumed per second", + "name": "Latency by Consumer Group", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "humantimeMilliseconds" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 7, + "i": "bfd08ec7-a539-4c5e-8499-4e5c437b97d7" + }, + "id": "bfd08ec7-a539-4c5e-8499-4e5c437b97d7" + }, + { + "targets": [ + { + "expr": "sum(rate(kafka_consumergroup_current_offset{instance=\"$instance\"}[1m])) by (topic)" + } + ], + "name": "Message consume per second", "options": { "tooltip": { "mode": "all", @@ -229,22 +235,21 @@ "type": "timeseries", "layout": { "h": 7, - "w": 8, - "x": 8, + "w": 12, + "x": 12, "y": 5, - "i": "7ad651a6-c12c-4d46-8d01-749fa776faef", - "isResizable": true + "i": "9a42427a-0e01-432e-838d-a6baca6c42b2" }, - "id": "7ad651a6-c12c-4d46-8d01-749fa776faef" + "id": "9a42427a-0e01-432e-838d-a6baca6c42b2" }, { "targets": [ { - "expr": "sum(kafka_consumer_lag_millis{cluster=\"$cluster\"}) by (consumergroup, topic)", + "expr": "sum(kafka_topic_partition_current_offset{instance=\"$instance\"}) by (topic) - sum(kafka_consumergroup_current_offset{instance=\"$instance\"}) by (topic) ", "legend": "{{consumergroup}} (topic: {{topic}})" } ], - "name": "Latency by Consumer Group", + "name": "Lag by Consumer Group", "options": { "tooltip": { "mode": "all", @@ -253,9 +258,7 @@ "legend": { "displayMode": "hidden" }, - "standardOptions": { - "util": "humantimeMilliseconds" - }, + "standardOptions": {}, "thresholds": {} }, "custom": { @@ -268,25 +271,23 @@ "type": "timeseries", "layout": { "h": 7, - "w": 8, - "x": 16, - "y": 5, - "i": "855aa8f5-0c51-42d4-b9a4-5460b7cd0f5a", - "isResizable": true + "w": 12, + "x": 12, + "y": 7, + "i": "7324f196-467b-4590-ae47-d56be683a0c3" }, - "id": "855aa8f5-0c51-42d4-b9a4-5460b7cd0f5a" + "id": "7324f196-467b-4590-ae47-d56be683a0c3" }, { - "id": "20166830-7f85-4665-8f39-bf904267af29", + "id": "bd4d2d51-7b4d-4523-b586-0bf2b248d4d4", "type": "row", "name": "patition/replicate", "layout": { "h": 1, "w": 24, "x": 0, - "y": 12, - "i": "20166830-7f85-4665-8f39-bf904267af29", - "isResizable": false + "y": 14, + "i": "bd4d2d51-7b4d-4523-b586-0bf2b248d4d4" }, "collapsed": true }, @@ -294,14 +295,13 @@ "targets": [ { "refId": "A", - "expr": "kafka_topic_partitions{cluster=\"$cluster\"}", + "expr": "kafka_topic_partitions{instance=\"$instance\"}", "legend": "{{topic}}" } ], "name": "Partitions per Topic", "custom": { "showHeader": true, - "colorMode": "value", "calc": "lastNotNull", "displayMode": "seriesToRows" }, @@ -317,25 +317,23 @@ "h": 7, "w": 12, "x": 0, - "y": 13, - "i": "8837a52e-c9eb-4afa-acc1-c3a5dac72d3b", - "isResizable": true + "y": 15, + "i": "04d1f6cc-40ec-4584-be17-a4d10cd5b6e9" }, - "id": "8837a52e-c9eb-4afa-acc1-c3a5dac72d3b" + "id": "04d1f6cc-40ec-4584-be17-a4d10cd5b6e9" }, { "targets": [ { "refId": "A", - "expr": "kafka_topic_partition_under_replicated_partition{cluster=\"$cluster\"}", + "expr": "kafka_topic_partition_under_replicated_partition", "legend": "{{topic}}-{{partition}}" } ], - "name": "Partitions Under Replicated", + "name": "Under Replicated", "description": "副本不同步预案\n1. Restart the Zookeeper leader.\n2. Restart the broker\\brokers that are not replicating some of the partitions.", "custom": { "showHeader": true, - "colorMode": "value", "calc": "lastNotNull", "displayMode": "seriesToRows" }, @@ -351,11 +349,10 @@ "h": 7, "w": 12, "x": 12, - "y": 13, - "i": "dd615767-dda7-4da6-b37f-0d484553aac6", - "isResizable": true + "y": 15, + "i": "5b589c1c-fd35-4ce5-8b24-c0e05d307345" }, - "id": "dd615767-dda7-4da6-b37f-0d484553aac6" + "id": "5b589c1c-fd35-4ce5-8b24-c0e05d307345" } ] } diff --git a/integrations/kafka/icon/kafka.svg b/integrations/kafka/icon/kafka.svg new file mode 100644 index 0000000000000000000000000000000000000000..91f4712005a6ed560d48387314474d7932f4e23e --- /dev/null +++ b/integrations/kafka/icon/kafka.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docker/n9eetc/alerts/linux_by_categraf.json b/integrations/linux/alerts/linux_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/linux_by_categraf.json rename to integrations/linux/alerts/linux_by_categraf.json diff --git a/docker/n9eetc/alerts/linux_by_exporter.json b/integrations/linux/alerts/linux_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/linux_by_exporter.json rename to integrations/linux/alerts/linux_by_exporter.json diff --git a/docker/n9eetc/alerts/linux_by_telegraf.json b/integrations/linux/alerts/linux_by_telegraf.json similarity index 100% rename from docker/n9eetc/alerts/linux_by_telegraf.json rename to integrations/linux/alerts/linux_by_telegraf.json diff --git a/docker/n9eetc/alerts/ntp_by_categraf.json b/integrations/linux/alerts/ntp_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/ntp_by_categraf.json rename to integrations/linux/alerts/ntp_by_categraf.json diff --git a/etc/dashboards/linux_by_categraf.json b/integrations/linux/dashboards/linux_by_categraf.json similarity index 100% rename from etc/dashboards/linux_by_categraf.json rename to integrations/linux/dashboards/linux_by_categraf.json diff --git a/integrations/linux/dashboards/linux_by_exporter.json b/integrations/linux/dashboards/linux_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..fe540b7e9fc1dd9834867394acb807936e0c78f7 --- /dev/null +++ b/integrations/linux/dashboards/linux_by_exporter.json @@ -0,0 +1,2064 @@ +{ + "name": "HOST - 模板", + "tags": "Prometheus Host", + "ident": "", + "configs": { + "var": [ + { + "name": "node", + "definition": "label_values(node_cpu_seconds_total, instance)", + "selected": "$node", + "options": [ + "tt-fc-es01.nj:12345", + "tt-fc-es02.nj:12345", + "tt-fc-dev01.nj:12345", + "10.206.0.13:9100" + ] + } + ], + "links": [ + { + "title": "n9e", + "url": "https://n9e.gitee.io/", + "targetBlank": true + }, + { + "title": "author", + "url": "http://flashcat.cloud/", + "targetBlank": true + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "3173366d-01a2-420e-8878-75124b0051b6", + "type": "row", + "name": "整体概况", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "3173366d-01a2-420e-8878-75124b0051b6" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10,100-(avg by (mode, instance)(rate(node_cpu_seconds_total{mode=\"idle\"}[1m])))*100)", + "legend": "{{instance}}" + } + ], + "name": "cpu使用率 top10", + "links": [], + "description": "", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 9, + "x": 3, + "y": 1, + "i": "e1925fc8-cb05-467b-ba82-bb5cb6be7595" + }, + "id": "e1925fc8-cb05-467b-ba82-bb5cb6be7595" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10,(node_memory_MemTotal_bytes - node_memory_MemFree_bytes - (node_memory_Cached_bytes + node_memory_Buffers_bytes))/node_memory_MemTotal_bytes*100)", + "legend": "{{instance}}" + } + ], + "name": "内存使用率 top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 1, + "i": "327b7e4b-6ec1-47e1-8840-d31cf4b5532b" + }, + "id": "327b7e4b-6ec1-47e1-8840-d31cf4b5532b" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10,(node_filesystem_avail_bytes{device!~'rootfs', device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"} * 100) / node_filesystem_size_bytes{device!~'rootfs', device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"})", + "legend": "{{instance}}-{{mountpoint}}" + } + ], + "name": "磁盘分区使用率 top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 2, + "i": "5a9d4a65-3f73-42cc-859e-fc0b82791b59" + }, + "id": "5a9d4a65-3f73-42cc-859e-fc0b82791b59" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10,rate(node_disk_io_time_seconds_total[5m]) * 100)", + "legend": "{{instance}}-{{device}}" + } + ], + "name": "设备io util top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 2, + "i": "fa764e4b-5ca9-45d8-b12e-604f8743f9d9" + }, + "id": "fa764e4b-5ca9-45d8-b12e-604f8743f9d9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "count(node_boot_time_seconds)" + } + ], + "name": "监控机器数", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 40 + } + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 0, + "y": 1, + "i": "9a5e3292-b346-4ccf-a793-b83a2f8ac8c5" + }, + "id": "9a5e3292-b346-4ccf-a793-b83a2f8ac8c5" + }, + { + "id": "396bf5e2-f204-4349-8e00-fb9d25ed7e79", + "type": "row", + "name": "单机概况", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 5, + "i": "396bf5e2-f204-4349-8e00-fb9d25ed7e79" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "(node_memory_MemTotal_bytes{instance=\"$node\"} - node_memory_MemFree_bytes{instance=\"$node\"} - (node_memory_Cached_bytes{instance=\"$node\"} + node_memory_Buffers_bytes{instance=\"$node\"}))/node_memory_MemTotal_bytes{instance=\"$node\"}*100" + } + ], + "name": "内存使用率", + "description": "如果内存使用率超过50%,则需要扩容或者升级配置了", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 25 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "result": { + "color": "#369903" + }, + "match": { + "from": 0, + "to": 50 + } + }, + { + "type": "range", + "match": { + "from": 50, + "to": 100 + }, + "result": { + "color": "#e3170d" + } + } + ], + "standardOptions": { + "util": "percent", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 6, + "i": "56dc011d-fc1c-4682-a903-1b778cbff9e8" + }, + "id": "56dc011d-fc1c-4682-a903-1b778cbff9e8" + }, + { + "targets": [ + { + "expr": "(((count(count(node_cpu_seconds_total{instance=\"$node\"}) by (cpu))) - avg(sum by (mode)(rate(node_cpu_seconds_total{mode='idle',instance=\"$node\"}[1m])))) * 100) / count(count(node_cpu_seconds_total{instance=\"$node\"}) by (cpu))" + } + ], + "name": "CPU使用率", + "description": "如果cpu使用率超过50%,可以通过top命令查看机器上是否有异常进程,如果没有异常进程,则说明服务需要扩容或者机器需要升级配置了", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 30 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "result": { + "color": "#369903" + }, + "match": { + "from": 0, + "to": 50 + } + }, + { + "type": "range", + "match": { + "special": 50, + "from": 50, + "to": 100 + }, + "result": { + "color": "#b22222" + } + } + ], + "standardOptions": { + "util": "percent", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 6, + "i": "639f4668-fb33-427d-8ec8-4f11127a1bf3" + }, + "id": "639f4668-fb33-427d-8ec8-4f11127a1bf3" + }, + { + "targets": [ + { + "expr": "max(100 - ((node_filesystem_avail_bytes{instance=\"$node\",} * 100) / node_filesystem_size_bytes{instance=\"$node\"}))", + "legend": "{{mountpoint}}" + } + ], + "name": "磁盘分区使用率最大值", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent", + "decimals": 1 + }, + "thresholds": { + "steps": [ + { + "value": 90, + "color": "#f90101" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 7, + "i": "981e2271-2c6c-4410-b3fb-73c35049c11a" + }, + "id": "981e2271-2c6c-4410-b3fb-73c35049c11a" + }, + { + "targets": [ + { + "expr": "node_time_seconds{instance=\"$node\"} - node_boot_time_seconds{instance=\"$node\"}" + } + ], + "name": "启动时长", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "title": null, + "value": 20 + } + }, + "options": { + "valueMappings": [], + "standardOptions": { + "util": "humantimeSeconds", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 21, + "y": 6, + "i": "534ca690-87e5-4c53-9c8a-d3afe0276bf5" + }, + "id": "534ca690-87e5-4c53-9c8a-d3afe0276bf5" + }, + { + "targets": [ + { + "expr": "(node_memory_SwapTotal_bytes{instance=\"$node\"} - node_memory_SwapFree_bytes{instance=\"$node\"})" + } + ], + "name": "SWAP内存使用", + "description": "swap使用过高,会影响系统io性能,如果内存够用但swap使用很高,可以调小swappiness的值", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 30 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "result": { + "color": "#369903" + }, + "match": { + "from": 0, + "to": 50 + } + }, + { + "type": "range", + "match": { + "special": 50, + "from": 50, + "to": 80 + }, + "result": { + "color": "#fb9b2d" + } + }, + { + "type": "range", + "match": { + "from": 80, + "to": 100000 + }, + "result": { + "color": "#d10000" + } + } + ], + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 12, + "y": 6, + "i": "69c96540-965b-4e87-9eb7-c24a0c974474" + }, + "id": "69c96540-965b-4e87-9eb7-c24a0c974474" + }, + { + "targets": [ + { + "expr": "rate(node_vmstat_oom_kill{instance=\"$node\"}[1m])", + "legend": "OOM" + } + ], + "name": "OOM次数", + "description": "大于0,说明有进程内存不够用了,需要考虑扩容或升级配置了", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 1 + }, + "thresholds": { + "steps": [ + { + "value": 1, + "color": "#f90101" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 7, + "i": "39715d51-4d18-4185-8584-68a4d44adf2b" + }, + "id": "39715d51-4d18-4185-8584-68a4d44adf2b" + }, + { + "targets": [ + { + "expr": "max(rate(node_disk_io_time_seconds_total{instance=\"$node\"}[5m]) * 100)", + "legend": "{{device}}" + } + ], + "name": "磁盘设备io util 最大值", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 7, + "i": "4a9ea87d-d650-43ff-bf1e-70f44afabace" + }, + "id": "4a9ea87d-d650-43ff-bf1e-70f44afabace" + }, + { + "targets": [ + { + "expr": "node_filefd_allocated{instance=\"$node\"}/node_filefd_maximum{instance=\"$node\"}*100" + } + ], + "name": "FD使用率", + "description": "如果超过80%,建议把文件描述符的最大个数调大,或者扩容", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 25 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "result": { + "color": "#369903" + }, + "match": { + "from": 0, + "to": 50 + } + }, + { + "type": "range", + "match": { + "special": 50, + "from": 50, + "to": 80 + }, + "result": { + "color": "#fb9b2d" + } + }, + { + "type": "range", + "match": { + "from": 80, + "to": 100 + }, + "result": { + "color": "#d10000" + } + } + ], + "standardOptions": { + "util": "percent", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 15, + "y": 6, + "i": "14caedd8-a1fd-412b-8c50-e35d3df57a2b" + }, + "id": "14caedd8-a1fd-412b-8c50-e35d3df57a2b" + }, + { + "targets": [ + { + "expr": "max(100 - ((node_filesystem_files_free{instance=\"$node\",mountpoint!~\"/var/lib/.*\",mountpoint!~\"/run/user.*\"} * 100) / node_filesystem_files{instance=\"$node\",mountpoint!~\"/var/lib/.*\",mountpoint!~\"/run/user.*\"}))", + "legend": "{{mountpoint}}" + } + ], + "name": "inode分区使用率最大值", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent", + "decimals": 1 + }, + "thresholds": { + "steps": [ + { + "value": 50, + "color": "#f90101" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 7, + "i": "82310aef-8db6-46bf-96a0-fcad68ae7d9e" + }, + "id": "82310aef-8db6-46bf-96a0-fcad68ae7d9e" + }, + { + "targets": [ + { + "expr": "sum(node_filesystem_device_error{instance=\"$node\",mountpoint!~\"/var/lib/.*\",mountpoint!~\"/run.*\"})", + "legend": "{{mountpoint}}" + } + ], + "name": "写文件错误数总和", + "custom": { + "textMode": "valueAndName", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 30 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 0, + "to": 0 + }, + "result": { + "color": "#369903" + } + }, + { + "type": "range", + "match": { + "from": 1, + "to": 10000 + }, + "result": { + "color": "#f0310f" + } + } + ], + "standardOptions": { + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 18, + "y": 6, + "i": "84b04d6b-1f97-47b8-86ff-77e6b1af4f1d" + }, + "id": "84b04d6b-1f97-47b8-86ff-77e6b1af4f1d" + }, + { + "id": "22df4dfe-6f93-4f44-b7ea-254a690922a5", + "type": "row", + "name": "系统指标", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 10, + "i": "22df4dfe-6f93-4f44-b7ea-254a690922a5" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "node_procs_running{instance=\"$node\"}", + "legend": "{{mountpoint}}" + } + ], + "name": "进程数", + "description": "进程数超过2000,可以考虑扩容了", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [ + { + "value": 2000, + "color": "#ff0000" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 11, + "i": "7c4fede9-18b6-4a45-9278-76b6f724716e" + }, + "id": "7c4fede9-18b6-4a45-9278-76b6f724716e" + }, + { + "targets": [ + { + "expr": "node_timex_offset_seconds{instance=\"$node\"}", + "legend": "ntp偏移" + } + ], + "name": "NTP偏移", + "description": "", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 11, + "i": "077b1181-00e2-44ce-9295-7528a8b829d5" + }, + "id": "077b1181-00e2-44ce-9295-7528a8b829d5" + }, + { + "targets": [ + { + "expr": "rate(node_intr_total{instance=\"$node\"}[1m])", + "legend": "Interrupts" + }, + { + "expr": "irate(node_context_switches_total{instance=\"$node\"}[1m])", + "legend": "context switches" + } + ], + "name": "上下文切换/中断", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 11, + "i": "28f96d86-79ed-4564-84c9-8e8c58d66985" + }, + "id": "28f96d86-79ed-4564-84c9-8e8c58d66985" + }, + { + "targets": [ + { + "expr": "node_entropy_available_bits{instance=\"$node\"}", + "legend": "entropy" + } + ], + "name": "熵池大小", + "description": "熵池太小 ,程序使用随机函数会阻塞,可以安装 rng-tools 工具增加熵池大小,可参考\nhttps://codeantenna.com/a/Ab6aMd3NSA ", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [ + { + "value": 100, + "color": "#f70202" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 11, + "i": "1a16c5ad-1a71-4771-9f50-f3dcc4524f71" + }, + "id": "1a16c5ad-1a71-4771-9f50-f3dcc4524f71" + }, + { + "id": "406a3fd8-52fb-4935-9971-d7a8f37437df", + "type": "row", + "name": "CPU详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 18, + "i": "406a3fd8-52fb-4935-9971-d7a8f37437df" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": " (avg by (mode)(rate(node_cpu_seconds_total{instance=\"$node\",mode!=\"idle\"}[1m])))*100", + "legend": "{{mode}}" + } + ], + "name": "CPU使用率详情", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 19, + "i": "bb2b0b33-2f78-428d-8847-d900d0bbdf25" + }, + "id": "bb2b0b33-2f78-428d-8847-d900d0bbdf25" + }, + { + "targets": [ + { + "expr": " (avg by (mode)(rate(node_cpu_seconds_total{instance=\"$node\",mode=\"idle\"}[1m])))*100", + "legend": "cpu_idle" + } + ], + "name": "CPU空闲率", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [ + { + "value": 10, + "color": "#f90101" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 19, + "i": "28f582ca-dd5c-41a8-8cc8-bcc88f755253" + }, + "id": "28f582ca-dd5c-41a8-8cc8-bcc88f755253" + }, + { + "targets": [ + { + "expr": "node_load1{instance=\"$node\"}", + "legend": "load1" + }, + { + "expr": "node_load5{instance=\"$node\"}", + "legend": "load5" + }, + { + "expr": "node_load15{instance=\"$node\"}", + "legend": "load15" + } + ], + "name": "CPU负载", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 19, + "i": "51e95b78-940e-4e5e-9cf5-7c8515209de0" + }, + "id": "51e95b78-940e-4e5e-9cf5-7c8515209de0" + }, + { + "id": "f3ab98b2-318b-451b-868e-d967555b7925", + "type": "row", + "name": "内存详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 26, + "i": "f3ab98b2-318b-451b-868e-d967555b7925" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "node_memory_HugePages_Total{instance=\"$node\"}", + "legend": "HugePages_Total" + }, + { + "expr": "node_memory_Hugepagesize_bytes{instance=\"$node\"}", + "legend": "HugePages_Size" + }, + { + "expr": "node_memory_HugePages_Surp{instance=\"$node\"}", + "legend": "HugePages_Surp " + }, + { + "expr": "node_memory_HugePages_Free{instance=\"$node\"}", + "legend": "HugePages_Free" + }, + { + "expr": "node_memory_HugePages_Rsvd{instance=\"$node\"}", + "legend": "HugePages_Rsvd" + }, + { + "expr": "node_memory_AnonHugePages_bytes{instance=\"$node\"}", + "legend": "AnonHugePages" + }, + { + "expr": "node_memory_Inactive_file_bytes{instance=\"$node\"}", + "legend": "Inactive_file" + }, + { + "expr": "node_memory_Inactive_anon_bytes{instance=\"$node\"}", + "legend": "Inactive_anon" + }, + { + "expr": "node_memory_Active_file_bytes{instance=\"$node\"}", + "legend": "Active_file" + }, + { + "expr": "node_memory_Active_anon_bytes{instance=\"$node\"}", + "legend": "Active_anon" + }, + { + "expr": "node_memory_Unevictable_bytes{instance=\"$node\"}", + "legend": "Unevictable" + }, + { + "expr": "node_memory_AnonPages_bytes{instance=\"$node\"}", + "legend": "AnonPages" + }, + { + "expr": "node_memory_Shmem_bytes{instance=\"$node\"}", + "legend": "Shmem" + }, + { + "expr": "node_memory_Mapped_bytes{instance=\"$node\"}", + "legend": "Mapped" + }, + { + "expr": "node_memory_Cached_bytes{instance=\"$node\"} ", + "legend": "Cache" + }, + { + "expr": "node_memory_SwapCached_bytes{instance=\"$node\"}", + "legend": "SwapCache" + }, + { + "expr": "node_memory_Mlocked_bytes{instance=\"$node\"}", + "legend": "Mlocked" + }, + { + "expr": "node_memory_Buffers_bytes{instance=\"$node\"}", + "legend": "Buffers" + } + ], + "name": "用户态内存使用", + "description": "内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) ", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.35, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 27, + "i": "6b084867-c1a4-4e7f-a0d7-5dd24524f82d" + }, + "id": "6b084867-c1a4-4e7f-a0d7-5dd24524f82d" + }, + { + "targets": [ + { + "expr": "node_memory_Slab_bytes{instance=\"$node\"}", + "legend": "Slab " + }, + { + "expr": "node_memory_SReclaimable_bytes{instance=\"$node\"}", + "legend": "SReclaimable " + }, + { + "expr": "node_memory_SUnreclaim_bytes{instance=\"$node\"}", + "legend": "SUnreclaim " + }, + { + "expr": "node_memory_VmallocUsed_bytes{instance=\"$node\"}", + "legend": "VmallocUsed" + }, + { + "expr": "node_memory_VmallocChunk_bytes{instance=\"$node\"}", + "legend": "VmallocChunk" + }, + { + "expr": "node_memory_KernelStack_bytes{instance=\"$node\"}", + "legend": "KernelStack" + }, + { + "expr": "node_memory_Bounce_bytes{instance=\"$node\"}", + "legend": "Bounce " + } + ], + "name": "内核态内存使用", + "description": "内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) ", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 27, + "i": "10b1dde7-7cad-4992-be3f-7e8c8aac8c03" + }, + "id": "10b1dde7-7cad-4992-be3f-7e8c8aac8c03" + }, + { + "targets": [ + { + "expr": "node_memory_DirectMap1G_bytes{instance=\"$node\"}", + "legend": "DirectMap1G" + }, + { + "expr": "node_memory_DirectMap2M_bytes{instance=\"$node\"}", + "legend": "DirectMap2M" + }, + { + "expr": "node_memory_DirectMap4k_bytes{instance=\"$node\"}", + "legend": "DirectMap4K" + } + ], + "name": "TLB效率", + "description": "/proc/meminfo中的DirectMap所统计的不是关于内存的使用,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的, 比如2MB,4MB,甚至1GB的内存页,视不同的硬件而定。”DirectMap4k”表示映射为4kB的内存数量, “DirectMap2M”表示映射为2MB的内存数量,以此类推。所以DirectMap其实是一个反映TLB效率的指标", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 29, + "i": "ea5480e7-ec20-41e5-a007-74c846fae91a" + }, + "id": "ea5480e7-ec20-41e5-a007-74c846fae91a" + }, + { + "targets": [ + { + "expr": "node_memory_NFS_Unstable_bytes{instance=\"$node\"}", + "legend": "NFS Unstable" + }, + { + "expr": "node_memory_Writeback_bytes{instance=\"$node\"}", + "legend": "memory_Writeback" + }, + { + "expr": "node_memory_Dirty_bytes{instance=\"$node\"}", + "legend": "memory_Dirty" + } + ], + "name": "dirty page", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 29, + "i": "8d31c2aa-38c9-434e-8c93-4cbae4e1bd8d" + }, + "id": "8d31c2aa-38c9-434e-8c93-4cbae4e1bd8d" + }, + { + "id": "88aeb766-214b-43a9-85f3-9ec6368f0da0", + "type": "row", + "name": "磁盘详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 36, + "i": "88aeb766-214b-43a9-85f3-9ec6368f0da0" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "node_filesystem_avail_bytes{instance=\"$node\",device!~'rootfs', device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - Available" + }, + { + "expr": "node_filesystem_free_bytes{instance=\"$node\",device!~'rootfs',device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - Free" + }, + { + "expr": "node_filesystem_size_bytes{instance=\"$node\",device!~'rootfs',device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - Total" + } + ], + "name": "磁盘空间", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": null + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 37, + "i": "d30cf6d8-b40b-4be3-bc97-fa71f788602f" + }, + "id": "d30cf6d8-b40b-4be3-bc97-fa71f788602f" + }, + { + "targets": [ + { + "expr": "node_filesystem_files{instance=\"$node\",device!~'rootfs',device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - total" + }, + { + "expr": "node_filesystem_files_free{instance=\"$node\",device!~'rootfs',device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - free" + } + ], + "name": "inode", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 37, + "i": "985e4bef-dc1e-4b8d-9e46-66edd4cada3c" + }, + "id": "985e4bef-dc1e-4b8d-9e46-66edd4cada3c" + }, + { + "targets": [ + { + "expr": "node_filefd_maximum{instance=\"$node\"}", + "legend": "Max open files" + }, + { + "expr": "node_filefd_allocated{instance=\"$node\"}", + "legend": "Open files" + } + ], + "name": "fd使用", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 37, + "i": "a6ca862a-f704-4ce9-8402-016468657093" + }, + "id": "a6ca862a-f704-4ce9-8402-016468657093" + }, + { + "targets": [ + { + "expr": "node_filesystem_readonly{instance=\"$node\",device!~'rootfs',device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - ReadOnly" + }, + { + "expr": "node_filesystem_device_error{instance=\"$node\",device!~'rootfs',device!~\"tmpfs\",mountpoint!~\"/var/lib.*\"}", + "legend": "{{mountpoint}} - Device error" + } + ], + "name": "读写错误", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 37, + "i": "362bfd2d-2ef2-49a0-b64e-14264495672c" + }, + "id": "362bfd2d-2ef2-49a0-b64e-14264495672c" + }, + { + "targets": [ + { + "expr": "rate(node_disk_reads_completed_total{instance=\"$node\"}[1m])", + "legend": "{{device}}-reads" + }, + { + "expr": "rate(node_disk_writes_completed_total{instance=\"$node\"}[1m])", + "legend": "{{device}} - Writes" + }, + { + "expr": "rate(node_disk_reads_merged_total{instance=\"$node\"}[1m])", + "legend": "{{device}} - Read merged" + }, + { + "expr": "rate(node_disk_writes_merged_total{instance=\"$node\"}[1m])", + "legend": "{{device}} - Write merged" + } + ], + "name": "IO/Merged次数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 39, + "i": "b118aac2-217d-4fb3-881a-7926f0e8078e" + }, + "id": "b118aac2-217d-4fb3-881a-7926f0e8078e" + }, + { + "targets": [ + { + "expr": "rate(node_disk_read_bytes_total{instance=\"$node\"}[1m])", + "legend": "{{device}}-Read bytes" + }, + { + "expr": "rate(node_disk_written_bytes_total{instance=\"$node\"}[1m])", + "legend": "{{device}} - Written bytes" + } + ], + "name": "读写数据大小", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 39, + "i": "b516483d-ea9b-4f4c-9fdc-5ac70fe159d2" + }, + "id": "b516483d-ea9b-4f4c-9fdc-5ac70fe159d2" + }, + { + "targets": [ + { + "expr": "rate(node_disk_io_time_seconds_total{instance=\"$node\"}[1m])", + "legend": "{{device}}" + } + ], + "name": "io util", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 39, + "i": "1572a36a-d5ac-403d-a5dd-21590bc8f5bf" + }, + "id": "1572a36a-d5ac-403d-a5dd-21590bc8f5bf" + }, + { + "targets": [ + { + "expr": "(rate(node_disk_read_time_seconds_total{instance=\"$node\"}[1m]) / rate(node_disk_reads_completed_total{instance=\"$node\"}[1m])+rate(node_disk_write_time_seconds_total{instance=\"$node\"}[1m]) / rate(node_disk_writes_completed_total{instance=\"$node\"}[1m]))*1000", + "legend": "{{device}}" + } + ], + "name": "io await", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.64, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 39, + "i": "c06c49fa-60ce-4982-bfe5-1d5012dc4af1" + }, + "id": "c06c49fa-60ce-4982-bfe5-1d5012dc4af1" + }, + { + "targets": [ + { + "expr": "(rate(node_disk_read_bytes_total{instance=\"$node\"}[1m]) + rate(node_disk_written_bytes_total{instance=\"$node\"}[1m]))\n/\n(rate(node_disk_reads_completed_total{instance=\"$node\"}[1m]) + rate(node_disk_writes_completed_total{instance=\"$node\"}[1m]))", + "legend": "avgrq-sz" + } + ], + "name": "avgrq-sz", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 41, + "i": "1c19b6ae-95fb-4358-bd01-bb0beec8d619" + }, + "id": "1c19b6ae-95fb-4358-bd01-bb0beec8d619" + }, + { + "targets": [ + { + "expr": "rate(node_disk_io_time_weighted_seconds_total{instance=\"$node\"}[1m])\n", + "legend": "{{device}}" + } + ], + "name": "avgqu-sz", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 41, + "i": "8da1e948-283d-4e68-990f-8e6c9af22d05" + }, + "id": "8da1e948-283d-4e68-990f-8e6c9af22d05" + }, + { + "id": "8fb70dca-4296-45a6-8dd3-770fc898ee65", + "type": "row", + "name": "网络详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 48, + "i": "8fb70dca-4296-45a6-8dd3-770fc898ee65" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "rate(node_network_receive_bytes_total{instance=\"$node\",device=~\"e.*\"}[1m])*8", + "legend": "{{device}} - in" + }, + { + "expr": "rate(node_network_transmit_bytes_total{instance=\"$node\",device=~\"e.*\"}[1m])*8", + "legend": "{{device}} - out" + } + ], + "name": "出入流量大小", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 49, + "i": "ae5d7236-c89f-4fb3-ae5c-e70cb03cb168" + }, + "id": "ae5d7236-c89f-4fb3-ae5c-e70cb03cb168" + }, + { + "targets": [ + { + "expr": "rate(node_network_receive_packets_total{instance=\"$node\",device=~\"e.*\"}[1m])", + "legend": "{{device}} - in" + }, + { + "expr": "rate(node_network_transmit_packets_total{instance=\"$node\",device=~\"e.*\"}[1m])", + "legend": "{{device}} - out" + } + ], + "name": "packets", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 49, + "i": "9cc72d79-c0bd-440d-849a-bf8af74a2b6c" + }, + "id": "9cc72d79-c0bd-440d-849a-bf8af74a2b6c" + }, + { + "targets": [ + { + "expr": "rate(node_network_receive_errs_total{instance=\"$node\",device=~\"e.*\"}[1m])", + "legend": "{{device}} - in" + }, + { + "expr": "rate(node_network_transmit_errs_total{instance=\"$node\",device=~\"e.*\"}[1m])", + "legend": "{{device}} - out" + } + ], + "name": "error", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 49, + "i": "8b54d503-daa0-4bea-bb00-d9c723123efd" + }, + "id": "8b54d503-daa0-4bea-bb00-d9c723123efd" + }, + { + "targets": [ + { + "expr": "rate(node_network_receive_drop_total{instance=\"$node\",device=~\"e.*\"}[1m])", + "legend": "{{device}} - in" + }, + { + "expr": "rate(node_network_transmit_drop_total{instance=\"$node\",device=~\"e.*\"}[1m])", + "legend": "{{device}} - out" + } + ], + "name": "drop", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 49, + "i": "293fc82f-1e25-412b-93cc-f8daceb61bd9" + }, + "id": "293fc82f-1e25-412b-93cc-f8daceb61bd9" + }, + { + "targets": [ + { + "expr": "node_nf_conntrack_entries{instance=\"$node\"}", + "legend": "NF conntrack entries" + }, + { + "expr": "node_nf_conntrack_entries_limit{instance=\"$node\"}", + "legend": "NF conntrack limit" + } + ], + "name": "nf_conntrack", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 51, + "i": "8fe595ad-f393-4d1c-a523-888071ca41b9" + }, + "id": "8fe595ad-f393-4d1c-a523-888071ca41b9" + }, + { + "targets": [ + { + "expr": "node_sockstat_TCP_alloc{instance=\"$node\"}", + "legend": "TCP_alloc" + }, + { + "expr": "node_sockstat_TCP_inuse{instance=\"$node\"}", + "legend": "TCP_inuse" + }, + { + "expr": "node_sockstat_TCP_orphan{instance=\"$node\"}", + "legend": "TCP_orphan" + }, + { + "expr": "node_sockstat_TCP_tw{instance=\"$node\"}", + "legend": "TCP_tw" + }, + { + "expr": "node_netstat_Tcp_CurrEstab{instance=\"$node\"}", + "legend": "TCP_CurrEstab" + } + ], + "name": "tcp", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.27, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 51, + "i": "64b815bc-b041-40f3-aaf2-21861836ab1d" + }, + "id": "64b815bc-b041-40f3-aaf2-21861836ab1d" + }, + { + "targets": [ + { + "expr": "node_sockstat_sockets_used{instance=\"$node\"}", + "legend": "Sockets_used" + } + ], + "name": "socket", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 51, + "i": "d089d8a2-dbbf-4dbf-89a8-b463522b1c1d" + }, + "id": "d089d8a2-dbbf-4dbf-89a8-b463522b1c1d" + } + ] + } +} \ No newline at end of file diff --git a/integrations/linux/dashboards/linux_by_telegraf.json b/integrations/linux/dashboards/linux_by_telegraf.json new file mode 100644 index 0000000000000000000000000000000000000000..ced6b432d141fd949e48f74372aca51f6a1ce53d --- /dev/null +++ b/integrations/linux/dashboards/linux_by_telegraf.json @@ -0,0 +1,1524 @@ +{ + "name": "HOST - Telegraf 模板", + "tags": "", + "ident": "", + "configs": { + "var": [ + { + "name": "ident", + "definition": "label_values(system_load1,ident)" + } + ], + "links": [ + { + "title": "n9e", + "url": "https://n9e.gitee.io/", + "targetBlank": true + }, + { + "title": "author", + "url": "http://flashcat.cloud/", + "targetBlank": true + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "0f6a1394-7cf9-4958-bcfe-2fbb59e77c12", + "type": "row", + "name": "整体概况", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "0f6a1394-7cf9-4958-bcfe-2fbb59e77c12" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "count(system_load1)" + } + ], + "name": "监控机器数", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 50 + } + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 0, + "y": 1, + "i": "877b6db5-e82c-499a-9ebc-8ad72c2891a8" + }, + "id": "877b6db5-e82c-499a-9ebc-8ad72c2891a8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10, mem_used_percent)" + } + ], + "name": "内存率 top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 9, + "x": 3, + "y": 1, + "i": "29a3e6ae-d278-49b3-972b-f12a6c7c091c" + }, + "id": "29a3e6ae-d278-49b3-972b-f12a6c7c091c" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10, (100-cpu_usage_idle{cpu=\"cpu-total\"}))" + } + ], + "name": "cpu使用率 top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 1, + "i": "9f2a24d5-d19f-4651-b76d-add6b9011821" + }, + "id": "9f2a24d5-d19f-4651-b76d-add6b9011821" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10, (disk_used_percent{path!~\"/var.*\"}))", + "legend": "{{ident}}-{{path}}" + } + ], + "name": "磁盘分区使用率 top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 2, + "i": "dcd60296-db84-4562-99f3-2829c2f064a4" + }, + "id": "dcd60296-db84-4562-99f3-2829c2f064a4" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(10, (rate(diskio_io_time[1m])/10))", + "legend": "" + } + ], + "name": "设备io util top10", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 2, + "i": "ef7df29d-7dce-4788-ae42-d21d842c67d6" + }, + "id": "ef7df29d-7dce-4788-ae42-d21d842c67d6" + }, + { + "id": "7b2c5cb2-fe3b-4596-95a1-37da06cd6498", + "type": "row", + "name": "单机概况", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 5, + "i": "7b2c5cb2-fe3b-4596-95a1-37da06cd6498" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "system_uptime{ident=\"$ident\"}" + } + ], + "name": "启动时长", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 30 + } + }, + "options": { + "valueMappings": [], + "standardOptions": { + "util": "humantimeSeconds", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 6, + "i": "50f09231-fc5e-4f6d-9367-a3158504689b" + }, + "id": "50f09231-fc5e-4f6d-9367-a3158504689b" + }, + { + "targets": [ + { + "refId": "A", + "expr": "100-cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}" + } + ], + "name": "CPU使用率", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 30 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 0, + "to": 50 + }, + "result": { + "color": "#129b22" + } + }, + { + "type": "range", + "match": { + "from": 50, + "to": 100 + }, + "result": { + "color": "#f51919" + } + } + ], + "standardOptions": { + "util": "percent", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 6, + "i": "d44e951d-c333-4ed9-9303-9c8d29da7993" + }, + "id": "d44e951d-c333-4ed9-9303-9c8d29da7993" + }, + { + "targets": [ + { + "refId": "A", + "expr": "mem_used_percent{ident=\"$ident\"}" + } + ], + "name": "内存使用率", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 30 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 0, + "to": 50 + }, + "result": { + "color": "#129b22" + } + }, + { + "type": "range", + "match": { + "from": 50, + "to": 100 + }, + "result": { + "color": "#f51919" + } + } + ], + "standardOptions": { + "util": "percent", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 6, + "i": "278c2fa1-0b19-4718-8b12-fb1c2e776258" + }, + "id": "278c2fa1-0b19-4718-8b12-fb1c2e776258" + }, + { + "targets": [ + { + "refId": "A", + "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}/linux_sysctl_fs_file_max{ident=\"$ident\"}*100" + } + ], + "name": "FD使用率", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 25 + } + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 0, + "to": 50 + }, + "result": { + "color": "#129b22" + } + }, + { + "type": "range", + "match": { + "from": 50, + "to": 100 + }, + "result": { + "color": "#f51919" + } + } + ], + "standardOptions": { + "util": "percent", + "decimals": 2 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 18, + "y": 6, + "i": "484afcd4-7b25-4af1-8e95-88cc675f7f43" + }, + "id": "484afcd4-7b25-4af1-8e95-88cc675f7f43" + }, + { + "targets": [ + { + "refId": "A", + "expr": "mem_swap_total{ident=\"$ident\"}-mem_swap_free{ident=\"$ident\"}" + } + ], + "name": "SWAP使用", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 40 + } + }, + "options": { + "valueMappings": [], + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 3, + "x": 21, + "y": 6, + "i": "142f63d7-4979-4354-81b5-a9c5ec81fae9" + }, + "id": "142f63d7-4979-4354-81b5-a9c5ec81fae9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "disk_used_percent{ident=\"$ident\"}", + "legend": "{{path}}" + } + ], + "name": "磁盘使用率", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 8, + "x": 0, + "y": 7, + "i": "4d6ec15c-8fdd-47db-a9f7-a57f03009e66" + }, + "id": "4d6ec15c-8fdd-47db-a9f7-a57f03009e66" + }, + { + "targets": [ + { + "refId": "A", + "expr": "disk_inodes_used{ident=\"$ident\"}/disk_inodes_total{ident=\"$ident\"}", + "legend": "{{path}}" + } + ], + "name": "inode使用率", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 8, + "x": 8, + "y": 7, + "i": "2991ad6b-c219-4f1d-b298-e195cf35cfec" + }, + "id": "2991ad6b-c219-4f1d-b298-e195cf35cfec" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(diskio_io_time{ident=\"$ident\"}[1m])/10", + "legend": "{{name}}" + } + ], + "name": "io_util", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 8, + "x": 16, + "y": 7, + "i": "935435db-5a1e-4330-b95f-825e91e9d99e" + }, + "id": "935435db-5a1e-4330-b95f-825e91e9d99e" + }, + { + "id": "1a19ca3f-3296-43ef-a36c-523ead023489", + "type": "row", + "name": "系统指标", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 10, + "i": "1a19ca3f-3296-43ef-a36c-523ead023489" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "processes_total{ident=\"$ident\"}" + } + ], + "name": "进程总数", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [ + { + "value": 2000, + "color": "#fa2a05" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 11, + "i": "bab39b5e-63cf-4e88-a474-4ea8f2585d8e" + }, + "id": "bab39b5e-63cf-4e88-a474-4ea8f2585d8e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(kernel_context_switches{ident=\"$ident\"}[1m])", + "legend": "context_switches" + }, + { + "expr": "rate(kernel_interrupts{ident=\"$ident\"}[1m])", + "refId": "B", + "legend": "kernel_interrupts" + } + ], + "name": "上下文切换/中断", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 11, + "i": "0ea75485-cc11-4b44-b13f-911429d9e103" + }, + "id": "0ea75485-cc11-4b44-b13f-911429d9e103" + }, + { + "targets": [ + { + "refId": "A", + "expr": "kernel_entropy_avail{ident=\"$ident\"}", + "legend": "entropy_avail" + } + ], + "name": "熵池大小", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [ + { + "value": 100, + "color": "#f50505" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 11, + "i": "32d764fa-ed86-4099-b0f8-1cb8c7f67315" + }, + "id": "32d764fa-ed86-4099-b0f8-1cb8c7f67315" + }, + { + "id": "fe779989-795e-4ef6-9280-fdea929bb397", + "type": "row", + "name": "CPU", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 18, + "i": "fe779989-795e-4ef6-9280-fdea929bb397" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "cpu_usage_idle{ident=\"$ident\",cpu=\"cpu-total\"}", + "legend": "cpu_usage_idle" + } + ], + "name": "CPU空闲率", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": { + "steps": [ + { + "value": 10, + "color": "#f20202" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 19, + "i": "f5b86c4f-2104-41a4-9d01-a62ee64c04ff" + }, + "id": "f5b86c4f-2104-41a4-9d01-a62ee64c04ff" + }, + { + "targets": [ + { + "refId": "A", + "expr": "cpu_usage_guest{ident=\"$ident\",cpu=\"cpu-total\"}", + "legend": "" + }, + { + "expr": "cpu_usage_iowait{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "B", + "legend": "" + }, + { + "expr": "cpu_usage_user{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "C" + }, + { + "expr": "cpu_usage_system{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "D" + }, + { + "expr": "cpu_usage_irq{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "E" + }, + { + "expr": "cpu_usage_softirq{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "F" + }, + { + "expr": "cpu_usage_nice{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "G" + }, + { + "expr": "cpu_usage_steal{ident=\"$ident\",cpu=\"cpu-total\"}", + "refId": "H" + } + ], + "name": "CPU使用率详情", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 19, + "i": "e833715f-065c-4b1b-9f0d-e1223b6992b8" + }, + "id": "e833715f-065c-4b1b-9f0d-e1223b6992b8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "system_load15{ident=\"$ident\"}" + }, + { + "expr": "system_load1{ident=\"$ident\"}", + "refId": "B" + }, + { + "expr": "system_load5{ident=\"$ident\"}", + "refId": "C" + } + ], + "name": "CPU负载", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 19, + "i": "b19f9420-9ce2-4d3c-86bf-fc247c8b760e" + }, + "id": "b19f9420-9ce2-4d3c-86bf-fc247c8b760e" + }, + { + "id": "e9ebdac6-4a87-4a79-b125-e5a258a968d0", + "type": "row", + "name": "内存详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 26, + "i": "e9ebdac6-4a87-4a79-b125-e5a258a968d0" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "mem_active{ident=\"$ident\"}" + }, + { + "expr": "mem_cached{ident=\"$ident\"}", + "refId": "B" + }, + { + "expr": "mem_buffered{ident=\"$ident\"}", + "refId": "C" + }, + { + "expr": "mem_inactive{ident=\"$ident\"}", + "refId": "D" + }, + { + "expr": "mem_mapped{ident=\"$ident\"}", + "refId": "E" + }, + { + "expr": "mem_shared{ident=\"$ident\"}", + "refId": "F" + }, + { + "expr": "mem_swap_cached{ident=\"$ident\"}", + "refId": "G" + } + ], + "name": "用户态内存使用", + "description": "内存指标可参考链接 [/PROC/MEMINFO之谜](http://linuxperf.com/?p=142) ", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 27, + "i": "655608d2-0d6d-46ed-9e6a-c482edaeacec" + }, + "id": "655608d2-0d6d-46ed-9e6a-c482edaeacec" + }, + { + "targets": [ + { + "refId": "A", + "expr": "mem_slab{ident=\"$ident\"}" + }, + { + "expr": "mem_sreclaimable{ident=\"$ident\"}", + "refId": "B" + }, + { + "expr": "mem_sunreclaim{ident=\"$ident\"}", + "refId": "C" + }, + { + "expr": "mem_vmalloc_used{ident=\"$ident\"}", + "refId": "D" + }, + { + "expr": "mem_vmalloc_chunk{ident=\"$ident\"}", + "refId": "E" + } + ], + "name": "内核态内存使用", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 27, + "i": "d57fe702-e21f-45ee-9b36-31695e698059" + }, + "id": "d57fe702-e21f-45ee-9b36-31695e698059" + }, + { + "id": "893315c8-54ac-4eaf-9072-d0a2debd3404", + "type": "row", + "name": "磁盘详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 34, + "i": "893315c8-54ac-4eaf-9072-d0a2debd3404" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "disk_free{ident=\"$ident\"}" + }, + { + "expr": "disk_total{ident=\"$ident\"}", + "refId": "B" + }, + { + "expr": "disk_used{ident=\"$ident\"}", + "refId": "C" + } + ], + "name": "磁盘空间", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": null + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 35, + "i": "b39143a6-9ca8-4732-9100-0a8c029440b5" + }, + "id": "b39143a6-9ca8-4732-9100-0a8c029440b5" + }, + { + "targets": [ + { + "refId": "A", + "expr": "linux_sysctl_fs_file_max{ident=\"$ident\"}" + }, + { + "expr": "linux_sysctl_fs_file_nr{ident=\"$ident\"}", + "refId": "B" + } + ], + "name": "fd使用", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 35, + "i": "3a13d95d-a311-4b0b-87f2-4216cac9a533" + }, + "id": "3a13d95d-a311-4b0b-87f2-4216cac9a533" + }, + { + "targets": [ + { + "refId": "A", + "expr": "disk_inodes_total{ident=\"$ident\",path!~\"/var.*\"}", + "legend": "{{path}}-total" + }, + { + "expr": "disk_inodes_used{ident=\"$ident\",path!~\"/var.*\"}", + "refId": "B", + "legend": "{{path}}-used" + } + ], + "name": "inode", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 35, + "i": "6a1fa455-a385-456a-a32b-30119082f453" + }, + "id": "6a1fa455-a385-456a-a32b-30119082f453" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(diskio_reads{ident=\"$ident\"}[1m])", + "legend": "{{name}}-read" + }, + { + "expr": "rate(diskio_writes{ident=\"$ident\"}[1m])", + "refId": "B", + "legend": "{{name}}-writes" + } + ], + "name": "IOPS", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 37, + "i": "c74e5155-0e3c-4cb4-8e57-ce8af46ddf90" + }, + "id": "c74e5155-0e3c-4cb4-8e57-ce8af46ddf90" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(diskio_read_bytes{ident=\"$ident\"}[1m])", + "legend": "{{name}}-read" + }, + { + "expr": "rate(diskio_write_bytes{ident=\"$ident\"}[1m])", + "refId": "B", + "legend": "{{name}}-writes" + } + ], + "name": "IO吞吐量", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 37, + "i": "c522e6f5-fb3d-4fc0-9ae6-7ec002e55e95" + }, + "id": "c522e6f5-fb3d-4fc0-9ae6-7ec002e55e95" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(diskio_write_time{ident=\"$ident\"}[1m])/rate(diskio_writes{ident=\"$ident\"}[1m])+rate(diskio_read_time{ident=\"$ident\"}[1m])/rate(diskio_reads{ident=\"$ident\"}[1m])", + "legend": "{{name}}" + } + ], + "name": "iowait", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 37, + "i": "47fd7f57-ed4a-43cf-86e3-628c2f697769" + }, + "id": "47fd7f57-ed4a-43cf-86e3-628c2f697769" + }, + { + "id": "f8c5e284-5e23-4646-976c-23511f4f908d", + "type": "row", + "name": "网络详情", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 44, + "i": "f8c5e284-5e23-4646-976c-23511f4f908d" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(net_bytes_recv{ident=\"$ident\",interface=~\"eth.*\"}[1m])*8", + "legend": "{{interface}}-recv" + }, + { + "expr": "rate(net_bytes_sent{ident=\"$ident\",interface=~\"eth.*\"}[1m])*8", + "refId": "B", + "legend": "{{interface}}-sent" + } + ], + "name": "网络流量", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 45, + "i": "7fe8774f-7d03-4514-a6b6-b626d2a95265" + }, + "id": "7fe8774f-7d03-4514-a6b6-b626d2a95265" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(net_packets_recv{ident=\"$ident\",interface=~\"eth.*\"}[1m])", + "legend": "{{interface}}-recv" + }, + { + "expr": "rate(net_packets_sent{ident=\"$ident\",interface=~\"eth.*\"}[1m])", + "refId": "B", + "legend": "{{interface}}-sent" + } + ], + "name": "packets", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 45, + "i": "d030e5e7-06cd-42e9-b1e5-0c32b51f853e" + }, + "id": "d030e5e7-06cd-42e9-b1e5-0c32b51f853e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(net_err_in{ident=\"$ident\",interface=~\"eth.*\"}[1m])", + "legend": "{{interface}}-in" + }, + { + "expr": "rate(net_err_out{ident=\"$ident\",interface=~\"eth.*\"}[1m])", + "refId": "B", + "legend": "{{interface}}-out" + } + ], + "name": "error", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 45, + "i": "330f5fd9-aca5-4619-b81b-33203256d560" + }, + "id": "330f5fd9-aca5-4619-b81b-33203256d560" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(net_drop_in{ident=\"$ident\",interface=~\"eth.*\"}[1m])", + "legend": "{{interface}}-in" + }, + { + "expr": "rate(net_drop_out{ident=\"$ident\",interface=~\"eth.*\"}[1m])", + "refId": "B", + "legend": "{{interface}}-out" + } + ], + "name": "drop", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 45, + "i": "34a73b20-56d7-4edb-b6f7-acc93d00a026" + }, + "id": "34a73b20-56d7-4edb-b6f7-acc93d00a026" + }, + { + "targets": [ + { + "refId": "A", + "expr": "netstat_tcp_established{ident=\"$ident\"}" + }, + { + "expr": "netstat_tcp_listen{ident=\"$ident\"}", + "refId": "B" + }, + { + "expr": "netstat_tcp_time_wait{ident=\"$ident\"}", + "refId": "C" + } + ], + "name": "tcp", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 47, + "i": "7664d34f-7bcf-4431-a6a7-4d924d2e176d" + }, + "id": "7664d34f-7bcf-4431-a6a7-4d924d2e176d" + } + ] + } +} \ No newline at end of file diff --git a/integrations/linux/icon/linux.png b/integrations/linux/icon/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..906c61b38a6cdb4148eaf8fcfc657e76f89417ac Binary files /dev/null and b/integrations/linux/icon/linux.png differ diff --git a/docker/n9eetc/alerts/mongo_by_exporter.json b/integrations/mongo/alerts/mongo_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/mongo_by_exporter.json rename to integrations/mongo/alerts/mongo_by_exporter.json diff --git a/etc/dashboards/mongo_by_exporter.json b/integrations/mongo/dashboards/mongo_by_exporter.json similarity index 100% rename from etc/dashboards/mongo_by_exporter.json rename to integrations/mongo/dashboards/mongo_by_exporter.json diff --git a/integrations/mongo/icon/mongodb.png b/integrations/mongo/icon/mongodb.png new file mode 100644 index 0000000000000000000000000000000000000000..eecd92a9dbc9e1e41fe7bde48dd7b818c5bc02f6 Binary files /dev/null and b/integrations/mongo/icon/mongodb.png differ diff --git a/docker/n9eetc/alerts/mysql_by_categraf.json b/integrations/mysql/alerts/mysql_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/mysql_by_categraf.json rename to integrations/mysql/alerts/mysql_by_categraf.json diff --git a/docker/n9eetc/alerts/mysql_by_exporter.json b/integrations/mysql/alerts/mysql_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/mysql_by_exporter.json rename to integrations/mysql/alerts/mysql_by_exporter.json diff --git a/integrations/mysql/dashboards/mysql_by_categraf.json b/integrations/mysql/dashboards/mysql_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..d1e317ca7bb4f9ff2487bf3df19891c9c0773326 --- /dev/null +++ b/integrations/mysql/dashboards/mysql_by_categraf.json @@ -0,0 +1,838 @@ +{ + "name": "MySQL Overview - 模板 by categraf", + "tags": "Prometheus MySQL", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(mysql_global_status_uptime, instance)" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "fe0e2a5d-4e82-4eaf-b13a-6d98aa6b6860", + "type": "row", + "name": "Basic Info", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "fe0e2a5d-4e82-4eaf-b13a-6d98aa6b6860" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "min(mysql_global_status_uptime{instance=~\"$instance\"})" + } + ], + "name": "MySQL Uptime", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 1800 + }, + "result": { + "color": "#ec7718" + } + }, + { + "type": "range", + "match": { + "from": 1800 + }, + "result": { + "color": "#369603" + } + } + ], + "standardOptions": { + "util": "humantimeSeconds" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "80079949-dbff-48fe-a1eb-54b646c30135" + }, + "id": "80079949-dbff-48fe-a1eb-54b646c30135" + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_queries{instance=~\"$instance\"}[5m])" + } + ], + "name": "Current QPS", + "description": "mysql_global_status_queries", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 100 + }, + "result": { + "color": "#05a31f" + } + }, + { + "type": "range", + "match": { + "from": 100 + }, + "result": { + "color": "#ea3939" + } + } + ], + "standardOptions": { + "decimals": 2 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "9fd6dd09-d131-4c0e-88ea-ed62c72baf97" + }, + "id": "9fd6dd09-d131-4c0e-88ea-ed62c72baf97" + }, + { + "targets": [ + { + "expr": "avg(mysql_global_variables_innodb_buffer_pool_size{instance=~\"$instance\"})" + } + ], + "name": "InnoDB Buffer Pool", + "description": "**InnoDB Buffer Pool Size**\n\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "bytesIEC" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "24913190-b86d-44b7-a8db-555351d9d3c2" + }, + "id": "24913190-b86d-44b7-a8db-555351d9d3c2" + }, + { + "targets": [ + { + "expr": "sum(increase(mysql_global_status_table_locks_waited{instance=~\"$instance\"}[5m]))" + } + ], + "name": "Table Locks Waited(5min)", + "description": "**Table Locks**\n\nMySQL takes a number of different locks for varying reasons. In this graph we see how many Table level locks MySQL has requested from the storage engine. In the case of InnoDB, many times the locks could actually be row locks as it only takes table level locks in a few specific cases.\n\nIt is most useful to compare Locks Immediate and Locks Waited. If Locks waited is rising, it means you have lock contention. Otherwise, Locks Immediate rising and falling is normal activity.", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 1 + }, + "result": { + "color": "#e70d0d" + } + }, + { + "type": "range", + "match": { + "to": 1 + }, + "result": { + "color": "#53b503" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "94a1e97e-2241-4e05-a9e9-a9b1e69d1070" + }, + "id": "94a1e97e-2241-4e05-a9e9-a9b1e69d1070" + }, + { + "id": "ca82d30f-8e0d-4caa-8a00-2ed9efe4ad85", + "type": "row", + "name": "Connections", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "ca82d30f-8e0d-4caa-8a00-2ed9efe4ad85" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(mysql_global_status_threads_connected{instance=~\"$instance\"})", + "legend": "Connections" + }, + { + "expr": "sum(mysql_global_status_max_used_connections{instance=~\"$instance\"})", + "legend": "Max Used Connections" + }, + { + "expr": "sum(mysql_global_variables_max_connections{instance=~\"$instance\"})", + "legend": "Max Connections" + }, + { + "expr": "sum(rate(mysql_global_status_aborted_connects{instance=~\"$instance\"}[5m]))", + "legend": "Aborted Connections" + } + ], + "name": "MySQL Connections", + "description": "**Max Connections** \n\nMax Connections is the maximum permitted number of simultaneous client connections. By default, this is 151. Increasing this value increases the number of file descriptors that mysqld requires. If the required number of descriptors are not available, the server reduces the value of Max Connections.\n\nmysqld actually permits Max Connections + 1 clients to connect. The extra connection is reserved for use by accounts that have the SUPER privilege, such as root.\n\nMax Used Connections is the maximum number of connections that have been in use simultaneously since the server started.\n\nConnections is the number of connection attempts (successful or not) to the MySQL server.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 5, + "i": "e2c85e72-0286-49bc-8ddb-5fba5f449b53" + }, + "id": "e2c85e72-0286-49bc-8ddb-5fba5f449b53" + }, + { + "targets": [ + { + "expr": "sum(mysql_global_status_threads_connected{instance=~\"$instance\"})", + "legend": "Threads Connected" + }, + { + "expr": "sum(mysql_global_status_threads_running{instance=~\"$instance\"})", + "legend": "Threads Running" + } + ], + "name": "MySQL Client Thread Activity", + "description": "Threads Connected is the number of open connections, while Threads Running is the number of threads not sleeping.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 5, + "i": "fbd43ac2-159d-4e55-8bc6-800d1bbfbd59" + }, + "id": "fbd43ac2-159d-4e55-8bc6-800d1bbfbd59" + }, + { + "id": "cb81def4-ac63-4d42-b66e-440f9061794b", + "type": "row", + "name": "Query Performance", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "cb81def4-ac63-4d42-b66e-440f9061794b" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_created_tmp_tables{instance=~\"$instance\"}[5m]))", + "legend": "Created Tmp Tables" + }, + { + "expr": "sum(rate(mysql_global_status_created_tmp_disk_tables{instance=~\"$instance\"}[5m]))", + "legend": "Created Tmp Disk Tables" + }, + { + "expr": "sum(rate(mysql_global_status_created_tmp_files{instance=~\"$instance\"}[5m]))", + "legend": "Created Tmp Files" + } + ], + "name": "MySQL Temporary Objects", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.64, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 13, + "i": "5fa65a30-a49b-457f-b46a-11d2029188bd" + }, + "id": "5fa65a30-a49b-457f-b46a-11d2029188bd" + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_select_full_join{ instance=~\"$instance\"}[5m]))", + "legend": "Select Full Join" + }, + { + "expr": "sum(rate(mysql_global_status_select_full_range_join{ instance=~\"$instance\"}[5m]))", + "legend": "Select Full Range Join" + }, + { + "expr": "sum(rate(mysql_global_status_select_range{ instance=~\"$instance\"}[5m]))", + "legend": "Select Range" + }, + { + "expr": "sum(rate(mysql_global_status_select_range_check{ instance=~\"$instance\"}[5m]))", + "legend": "Select Range Check" + }, + { + "expr": "sum(rate(mysql_global_status_select_scan{ instance=~\"$instance\"}[5m]))", + "legend": "Select Scan" + } + ], + "name": "MySQL Select Types", + "description": "**MySQL Select Types**\n\nAs with most relational databases, selecting based on indexes is more efficient than scanning an entire table's data. Here we see the counters for selects not done with indexes.\n\n* ***Select Scan*** is how many queries caused full table scans, in which all the data in the table had to be read and either discarded or returned.\n* ***Select Range*** is how many queries used a range scan, which means MySQL scanned all rows in a given range.\n* ***Select Full Join*** is the number of joins that are not joined on an index, this is usually a huge performance hit.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "stack": "off", + "lineInterpolation": "smooth", + "fillOpacity": 0.41 + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 13, + "i": "20efd251-6207-4cec-aa3b-4351e8e9b125" + }, + "id": "20efd251-6207-4cec-aa3b-4351e8e9b125" + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_sort_rows{instance=~\"$instance\"}[5m]))", + "legend": "Sort Rows" + }, + { + "expr": "sum(rate(mysql_global_status_sort_range{instance=~\"$instance\"}[5m]))", + "legend": "Sort Range" + }, + { + "expr": "sum(rate(mysql_global_status_sort_merge_passes{instance=~\"$instance\"}[5m]))", + "legend": "Sort Merge Passes" + }, + { + "expr": "sum(rate(mysql_global_status_sort_scan{instance=~\"$instance\"}[5m]))", + "legend": "Sort Scan" + } + ], + "name": "MySQL Sorts", + "description": "**MySQL Sorts**\n\nDue to a query's structure, order, or other requirements, MySQL sorts the rows before returning them. For example, if a table is ordered 1 to 10 but you want the results reversed, MySQL then has to sort the rows to return 10 to 1.\n\nThis graph also shows when sorts had to scan a whole table or a given range of a table in order to return the results and which could not have been sorted via an index.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 15, + "i": "a4d0c5fb-04e0-4627-8722-ae996d70e2aa" + }, + "id": "a4d0c5fb-04e0-4627-8722-ae996d70e2aa" + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_slow_queries{instance=~\"$instance\"}[5m]))", + "legend": "Slow Queries" + } + ], + "name": "MySQL Slow Queries", + "description": "**MySQL Slow Queries**\n\nSlow queries are defined as queries being slower than the long_query_time setting. For example, if you have long_query_time set to 3, all queries that take longer than 3 seconds to complete will show on this graph.", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "bars", + "stack": "off", + "fillOpacity": 0.81 + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 15, + "i": "2e13ada4-1128-440d-9360-028f16c3779b" + }, + "id": "2e13ada4-1128-440d-9360-028f16c3779b" + }, + { + "id": "c9df805c-8ae7-41d7-b28b-575f478fd9ce", + "type": "row", + "name": "Network", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 22, + "i": "c9df805c-8ae7-41d7-b28b-575f478fd9ce" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_bytes_received{instance=~\"$instance\"}[5m]))", + "legend": "Inbound" + }, + { + "expr": "sum(rate(mysql_global_status_bytes_sent{instance=~\"$instance\"}[5m]))", + "legend": "Outbound" + } + ], + "name": "MySQL Network Traffic", + "description": "**MySQL Network Traffic**\n\nHere we can see how much network traffic is generated by MySQL. Outbound is network traffic sent from MySQL and Inbound is network traffic MySQL has received.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesSI", + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 23, + "i": "6107714f-bedd-437c-b6e4-d6eb74db6d30" + }, + "id": "6107714f-bedd-437c-b6e4-d6eb74db6d30" + }, + { + "id": "00fd2b70-a133-4ad7-bd56-69a3c91ecf0c", + "type": "row", + "name": "Commands, Handlers", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 30, + "i": "00fd2b70-a133-4ad7-bd56-69a3c91ecf0c" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "topk(10, rate(mysql_global_status_commands_total{instance=~\"$instance\"}[5m])>0)", + "legend": "Com_{{command}}" + } + ], + "name": "Top Command Counters", + "description": "**Top Command Counters**\n\nThe Com_{{xxx}} statement counter variables indicate the number of times each xxx statement has been executed. There is one status variable for each type of statement. For example, Com_delete and Com_update count [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements, respectively. Com_delete_multi and Com_update_multi are similar but apply to [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements that use multiple-table syntax.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.2, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 31, + "i": "f90ca2bc-0809-45f6-88b6-e258805def04" + }, + "id": "f90ca2bc-0809-45f6-88b6-e258805def04" + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_handlers_total{instance=~\"$instance\", handler!~\"commit|rollback|savepoint.*|prepare\"}[5m])", + "legend": "{{handler}}" + } + ], + "name": "MySQL Handlers", + "description": "**MySQL Handlers**\n\nHandler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes.\n\nThis is in fact the layer between the Storage Engine and MySQL.\n\n* `read_rnd_next` is incremented when the server performs a full table scan and this is a counter you don't really want to see with a high value.\n* `read_key` is incremented when a read is done with an index.\n* `read_next` is incremented when the storage engine is asked to 'read the next index entry'. A high value means a lot of index scans are being done.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 3 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 33, + "i": "74e1844d-a918-48fa-a29f-6535dc087dac" + }, + "id": "74e1844d-a918-48fa-a29f-6535dc087dac" + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_handlers_total{instance=~\"$instance\", handler=~\"commit|rollback|savepoint.*|prepare\"}[5m])", + "legend": "{{handler}}" + } + ], + "name": "MySQL Transaction Handlers", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 33, + "i": "b2c3a13d-898f-407b-b6a9-db852072b12f" + }, + "id": "b2c3a13d-898f-407b-b6a9-db852072b12f" + }, + { + "id": "c32a02da-6c61-4b9e-9365-c0b56088fabc", + "type": "row", + "name": "Open Files", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 40, + "i": "c32a02da-6c61-4b9e-9365-c0b56088fabc" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "mysql_global_variables_open_files_limit{instance=~\"$instance\"}", + "legend": "Open Files Limit" + }, + { + "expr": "mysql_global_status_open_files{instance=~\"$instance\"}", + "legend": "Open Files" + } + ], + "name": "MySQL Open Files", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 41, + "i": "fc13eadb-890d-4184-ac16-943d54188db8" + }, + "id": "fc13eadb-890d-4184-ac16-943d54188db8" + }, + { + "id": "6f596e65-3e4b-4d9a-aad7-a32c8c7b8239", + "type": "row", + "name": "Table Openings", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 48, + "i": "6f596e65-3e4b-4d9a-aad7-a32c8c7b8239" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_table_open_cache_hits{instance=~\"$instance\"}[5m])\n/\n(\nrate(mysql_global_status_table_open_cache_hits{instance=~\"$instance\"}[5m])\n+\nrate(mysql_global_status_table_open_cache_misses{instance=~\"$instance\"}[5m])\n)", + "legend": "Table Open Cache Hit Ratio" + } + ], + "name": "Table Open Cache Hit Ratio Mysql 5.6.6+", + "description": "**MySQL Table Open Cache Status**\n\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\n\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percentUnit" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 49, + "i": "0b78fbb5-a0b4-4a1b-98b1-af15bc91779d" + }, + "id": "0b78fbb5-a0b4-4a1b-98b1-af15bc91779d" + }, + { + "targets": [ + { + "expr": "mysql_global_status_open_tables{instance=~\"$instance\"}", + "legend": "Open Tables" + }, + { + "expr": "mysql_global_variables_table_open_cache{instance=~\"$instance\"}", + "legend": "Table Open Cache" + } + ], + "name": "MySQL Open Tables", + "description": "**MySQL Open Tables**\n\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\n\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 49, + "i": "948ad10b-8b22-4d42-9e94-99ef09e12927" + }, + "id": "948ad10b-8b22-4d42-9e94-99ef09e12927" + } + ] + } +} \ No newline at end of file diff --git a/integrations/mysql/dashboards/mysql_by_exporter.json b/integrations/mysql/dashboards/mysql_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..9575c9a3beb02192e398e753d77a999146fc415a --- /dev/null +++ b/integrations/mysql/dashboards/mysql_by_exporter.json @@ -0,0 +1,838 @@ +{ + "name": "MySQL Overview - 模板 by exporter", + "tags": "Prometheus MySQL", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(mysql_global_status_uptime, instance)" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "a94506f9-879c-41d4-bf0a-0ce479352742", + "type": "row", + "name": "Basic Info", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "a94506f9-879c-41d4-bf0a-0ce479352742" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "min(mysql_global_status_uptime{instance=~\"$instance\"})" + } + ], + "name": "MySQL Uptime", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 1800 + }, + "result": { + "color": "#ec7718" + } + }, + { + "type": "range", + "match": { + "from": 1800 + }, + "result": { + "color": "#369603" + } + } + ], + "standardOptions": { + "util": "humantimeSeconds" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "c1ed017a-86d8-4ba5-8e75-ce3be943eef9" + }, + "id": "c1ed017a-86d8-4ba5-8e75-ce3be943eef9" + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_queries{instance=~\"$instance\"}[5m])" + } + ], + "name": "Current QPS", + "description": "mysql_global_status_queries", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 100 + }, + "result": { + "color": "#05a31f" + } + }, + { + "type": "range", + "match": { + "from": 100 + }, + "result": { + "color": "#ea3939" + } + } + ], + "standardOptions": { + "decimals": 2 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "05b0a593-7328-4298-9b5c-af6bd6a34e52" + }, + "id": "05b0a593-7328-4298-9b5c-af6bd6a34e52" + }, + { + "targets": [ + { + "expr": "avg(mysql_global_variables_innodb_buffer_pool_size{instance=~\"$instance\"})" + } + ], + "name": "InnoDB Buffer Pool", + "description": "**InnoDB Buffer Pool Size**\n\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "bytesIEC" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "e5388f85-8970-4f64-83e1-e77d4025f1dd" + }, + "id": "e5388f85-8970-4f64-83e1-e77d4025f1dd" + }, + { + "targets": [ + { + "expr": "sum(increase(mysql_global_status_table_locks_waited{instance=~\"$instance\"}[5m]))" + } + ], + "name": "Table Locks Waited(5min)", + "description": "**Table Locks**\n\nMySQL takes a number of different locks for varying reasons. In this graph we see how many Table level locks MySQL has requested from the storage engine. In the case of InnoDB, many times the locks could actually be row locks as it only takes table level locks in a few specific cases.\n\nIt is most useful to compare Locks Immediate and Locks Waited. If Locks waited is rising, it means you have lock contention. Otherwise, Locks Immediate rising and falling is normal activity.", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 1 + }, + "result": { + "color": "#e70d0d" + } + }, + { + "type": "range", + "match": { + "to": 1 + }, + "result": { + "color": "#53b503" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "ab8a768e-98f3-4215-bfbf-ea838a12b45c" + }, + "id": "ab8a768e-98f3-4215-bfbf-ea838a12b45c" + }, + { + "id": "24a1be60-6b90-483a-af6f-48cc79830da1", + "type": "row", + "name": "Connections", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "24a1be60-6b90-483a-af6f-48cc79830da1" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(mysql_global_status_threads_connected{instance=~\"$instance\"})", + "legend": "Connections" + }, + { + "expr": "sum(mysql_global_status_max_used_connections{instance=~\"$instance\"})", + "legend": "Max Used Connections" + }, + { + "expr": "sum(mysql_global_variables_max_connections{instance=~\"$instance\"})", + "legend": "Max Connections" + }, + { + "expr": "sum(rate(mysql_global_status_aborted_connects{instance=~\"$instance\"}[5m]))", + "legend": "Aborted Connections" + } + ], + "name": "MySQL Connections", + "description": "**Max Connections** \n\nMax Connections is the maximum permitted number of simultaneous client connections. By default, this is 151. Increasing this value increases the number of file descriptors that mysqld requires. If the required number of descriptors are not available, the server reduces the value of Max Connections.\n\nmysqld actually permits Max Connections + 1 clients to connect. The extra connection is reserved for use by accounts that have the SUPER privilege, such as root.\n\nMax Used Connections is the maximum number of connections that have been in use simultaneously since the server started.\n\nConnections is the number of connection attempts (successful or not) to the MySQL server.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 5, + "i": "bb31cf5e-1a80-478c-b300-ee9975d14963" + }, + "id": "bb31cf5e-1a80-478c-b300-ee9975d14963" + }, + { + "targets": [ + { + "expr": "sum(mysql_global_status_threads_connected{instance=~\"$instance\"})", + "legend": "Threads Connected" + }, + { + "expr": "sum(mysql_global_status_threads_running{instance=~\"$instance\"})", + "legend": "Threads Running" + } + ], + "name": "MySQL Client Thread Activity", + "description": "Threads Connected is the number of open connections, while Threads Running is the number of threads not sleeping.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 5, + "i": "c1083f59-1e46-442e-a7c3-f5d1fbb78751" + }, + "id": "c1083f59-1e46-442e-a7c3-f5d1fbb78751" + }, + { + "id": "e126f7dd-df38-4a43-846a-ea6188718de9", + "type": "row", + "name": "Query Performance", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "e126f7dd-df38-4a43-846a-ea6188718de9" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_created_tmp_tables{instance=~\"$instance\"}[5m]))", + "legend": "Created Tmp Tables" + }, + { + "expr": "sum(rate(mysql_global_status_created_tmp_disk_tables{instance=~\"$instance\"}[5m]))", + "legend": "Created Tmp Disk Tables" + }, + { + "expr": "sum(rate(mysql_global_status_created_tmp_files{instance=~\"$instance\"}[5m]))", + "legend": "Created Tmp Files" + } + ], + "name": "MySQL Temporary Objects", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.64, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 13, + "i": "80f94d89-babe-4e38-a220-2490af80e091" + }, + "id": "80f94d89-babe-4e38-a220-2490af80e091" + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_select_full_join{ instance=~\"$instance\"}[5m]))", + "legend": "Select Full Join" + }, + { + "expr": "sum(rate(mysql_global_status_select_full_range_join{ instance=~\"$instance\"}[5m]))", + "legend": "Select Full Range Join" + }, + { + "expr": "sum(rate(mysql_global_status_select_range{ instance=~\"$instance\"}[5m]))", + "legend": "Select Range" + }, + { + "expr": "sum(rate(mysql_global_status_select_range_check{ instance=~\"$instance\"}[5m]))", + "legend": "Select Range Check" + }, + { + "expr": "sum(rate(mysql_global_status_select_scan{ instance=~\"$instance\"}[5m]))", + "legend": "Select Scan" + } + ], + "name": "MySQL Select Types", + "description": "**MySQL Select Types**\n\nAs with most relational databases, selecting based on indexes is more efficient than scanning an entire table's data. Here we see the counters for selects not done with indexes.\n\n* ***Select Scan*** is how many queries caused full table scans, in which all the data in the table had to be read and either discarded or returned.\n* ***Select Range*** is how many queries used a range scan, which means MySQL scanned all rows in a given range.\n* ***Select Full Join*** is the number of joins that are not joined on an index, this is usually a huge performance hit.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "stack": "off", + "lineInterpolation": "smooth", + "fillOpacity": 0.41 + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 13, + "i": "a03b6272-cd60-430c-8128-6bfc8da2938f" + }, + "id": "a03b6272-cd60-430c-8128-6bfc8da2938f" + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_sort_rows{instance=~\"$instance\"}[5m]))", + "legend": "Sort Rows" + }, + { + "expr": "sum(rate(mysql_global_status_sort_range{instance=~\"$instance\"}[5m]))", + "legend": "Sort Range" + }, + { + "expr": "sum(rate(mysql_global_status_sort_merge_passes{instance=~\"$instance\"}[5m]))", + "legend": "Sort Merge Passes" + }, + { + "expr": "sum(rate(mysql_global_status_sort_scan{instance=~\"$instance\"}[5m]))", + "legend": "Sort Scan" + } + ], + "name": "MySQL Sorts", + "description": "**MySQL Sorts**\n\nDue to a query's structure, order, or other requirements, MySQL sorts the rows before returning them. For example, if a table is ordered 1 to 10 but you want the results reversed, MySQL then has to sort the rows to return 10 to 1.\n\nThis graph also shows when sorts had to scan a whole table or a given range of a table in order to return the results and which could not have been sorted via an index.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 15, + "i": "d5fbfe0e-fc90-4f2a-b016-7a24a19c73d7" + }, + "id": "d5fbfe0e-fc90-4f2a-b016-7a24a19c73d7" + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_slow_queries{instance=~\"$instance\"}[5m]))", + "legend": "Slow Queries" + } + ], + "name": "MySQL Slow Queries", + "description": "**MySQL Slow Queries**\n\nSlow queries are defined as queries being slower than the long_query_time setting. For example, if you have long_query_time set to 3, all queries that take longer than 3 seconds to complete will show on this graph.", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "bars", + "stack": "off", + "fillOpacity": 0.81 + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 15, + "i": "51306ae6-e11a-4c08-a55c-3678676d5d8e" + }, + "id": "51306ae6-e11a-4c08-a55c-3678676d5d8e" + }, + { + "id": "867ae6c9-b4a4-4349-8e68-56ef9cebf8b4", + "type": "row", + "name": "Network", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 22, + "i": "867ae6c9-b4a4-4349-8e68-56ef9cebf8b4" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(rate(mysql_global_status_bytes_received{instance=~\"$instance\"}[5m]))", + "legend": "Inbound" + }, + { + "expr": "sum(rate(mysql_global_status_bytes_sent{instance=~\"$instance\"}[5m]))", + "legend": "Outbound" + } + ], + "name": "MySQL Network Traffic", + "description": "**MySQL Network Traffic**\n\nHere we can see how much network traffic is generated by MySQL. Outbound is network traffic sent from MySQL and Inbound is network traffic MySQL has received.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesSI", + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 23, + "i": "392c15b2-d413-4201-9692-5277f7863c05" + }, + "id": "392c15b2-d413-4201-9692-5277f7863c05" + }, + { + "id": "e58cb79a-75f2-452f-bc55-b36ff93a60c4", + "type": "row", + "name": "Commands, Handlers", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 30, + "i": "e58cb79a-75f2-452f-bc55-b36ff93a60c4" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "topk(10, rate(mysql_global_status_commands_total{instance=~\"$instance\"}[5m])>0)", + "legend": "Com_{{command}}" + } + ], + "name": "Top Command Counters", + "description": "**Top Command Counters**\n\nThe Com_{{xxx}} statement counter variables indicate the number of times each xxx statement has been executed. There is one status variable for each type of statement. For example, Com_delete and Com_update count [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements, respectively. Com_delete_multi and Com_update_multi are similar but apply to [``DELETE``](https://dev.mysql.com/doc/refman/5.7/en/delete.html) and [``UPDATE``](https://dev.mysql.com/doc/refman/5.7/en/update.html) statements that use multiple-table syntax.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.2, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 31, + "i": "df2f62e6-5a75-4cea-9268-3077348a6558" + }, + "id": "df2f62e6-5a75-4cea-9268-3077348a6558" + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_handlers_total{instance=~\"$instance\", handler!~\"commit|rollback|savepoint.*|prepare\"}[5m])", + "legend": "{{handler}}" + } + ], + "name": "MySQL Handlers", + "description": "**MySQL Handlers**\n\nHandler statistics are internal statistics on how MySQL is selecting, updating, inserting, and modifying rows, tables, and indexes.\n\nThis is in fact the layer between the Storage Engine and MySQL.\n\n* `read_rnd_next` is incremented when the server performs a full table scan and this is a counter you don't really want to see with a high value.\n* `read_key` is incremented when a read is done with an index.\n* `read_next` is incremented when the storage engine is asked to 'read the next index entry'. A high value means a lot of index scans are being done.", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 3 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 33, + "i": "34ba0da1-d6f0-4c35-8418-56a7506035c5" + }, + "id": "34ba0da1-d6f0-4c35-8418-56a7506035c5" + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_handlers_total{instance=~\"$instance\", handler=~\"commit|rollback|savepoint.*|prepare\"}[5m])", + "legend": "{{handler}}" + } + ], + "name": "MySQL Transaction Handlers", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 33, + "i": "9e37aa84-a6b6-4730-9fa7-0dab9e596e36" + }, + "id": "9e37aa84-a6b6-4730-9fa7-0dab9e596e36" + }, + { + "id": "779fdf9a-fcf8-4454-91a4-608950d3fba1", + "type": "row", + "name": "Open Files", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 40, + "i": "779fdf9a-fcf8-4454-91a4-608950d3fba1" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "mysql_global_variables_open_files_limit{instance=~\"$instance\"}", + "legend": "Open Files Limit" + }, + { + "expr": "mysql_global_status_innodb_num_open_files{instance=~\"$instance\"}", + "legend": "InnoDB Open Files" + } + ], + "name": "MySQL Open Files", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 41, + "i": "ac797cf1-56f6-4cf7-a472-8a2facd84588" + }, + "id": "ac797cf1-56f6-4cf7-a472-8a2facd84588" + }, + { + "id": "292f69d6-1a6c-463e-8aaf-14715b447c1f", + "type": "row", + "name": "Table Openings", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 48, + "i": "292f69d6-1a6c-463e-8aaf-14715b447c1f" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "rate(mysql_global_status_table_open_cache_hits{instance=~\"$instance\"}[5m])\n/\n(\nrate(mysql_global_status_table_open_cache_hits{instance=~\"$instance\"}[5m])\n+\nrate(mysql_global_status_table_open_cache_misses{instance=~\"$instance\"}[5m])\n)", + "legend": "Table Open Cache Hit Ratio" + } + ], + "name": "Table Open Cache Hit Ratio", + "description": "**MySQL Table Open Cache Status**\n\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\n\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percentUnit" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 49, + "i": "0139a750-1a56-45ee-9004-7a8ef15d34dd" + }, + "id": "0139a750-1a56-45ee-9004-7a8ef15d34dd" + }, + { + "targets": [ + { + "expr": "mysql_global_status_open_tables{instance=~\"$instance\"}", + "legend": "Open Tables" + }, + { + "expr": "mysql_global_variables_table_open_cache{instance=~\"$instance\"}", + "legend": "Table Open Cache" + } + ], + "name": "MySQL Open Tables", + "description": "**MySQL Open Tables**\n\nThe recommendation is to set the `table_open_cache_instances` to a loose correlation to virtual CPUs, keeping in mind that more instances means the cache is split more times. If you have a cache set to 500 but it has 10 instances, each cache will only have 50 cached.\n\nThe `table_definition_cache` and `table_open_cache` can be left as default as they are auto-sized MySQL 5.6 and above (ie: do not set them to any value).", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 49, + "i": "fba77c7e-9e40-4829-89b6-ed8bb2a7add7" + }, + "id": "fba77c7e-9e40-4829-89b6-ed8bb2a7add7" + } + ] + } +} \ No newline at end of file diff --git a/integrations/mysql/icon/mysql_logo.svg b/integrations/mysql/icon/mysql_logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..6d1d5c81e8533dd91af327a3a78f012fb64e4118 --- /dev/null +++ b/integrations/mysql/icon/mysql_logo.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/dashboards/n9e_server.json b/integrations/n9e/dashboards/n9e_server.json similarity index 100% rename from etc/dashboards/n9e_server.json rename to integrations/n9e/dashboards/n9e_server.json diff --git a/integrations/n9e/icon/nightingale_logo.svg b/integrations/n9e/icon/nightingale_logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..20d45c753bed38779fa48e205fb81581176fd58f --- /dev/null +++ b/integrations/n9e/icon/nightingale_logo.svg @@ -0,0 +1,16 @@ + + + Nightingale + + + + + + + + + + + + + \ No newline at end of file diff --git a/docker/n9eetc/alerts/net_response_by_categraf.json b/integrations/net_response/alerts/net_response_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/net_response_by_categraf.json rename to integrations/net_response/alerts/net_response_by_categraf.json diff --git a/integrations/net_response/dashboards/net_response_by_categraf.json b/integrations/net_response/dashboards/net_response_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..175793adf9afb932bc44e2fef908b51a3351f859 --- /dev/null +++ b/integrations/net_response/dashboards/net_response_by_categraf.json @@ -0,0 +1,91 @@ +{ + "name": "TCP探测", + "tags": "", + "ident": "", + "configs": { + "version": "2.0.0", + "panels": [ + { + "id": "b90370ef-ee1c-40c3-a570-e26a89448209", + "type": "row", + "name": "Default chart group", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "b90370ef-ee1c-40c3-a570-e26a89448209" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "max(net_response_result_code) by (target)", + "legend": "UP?" + }, + { + "expr": "max(net_response_response_time) by (target)", + "refId": "C", + "legend": "latency(s)" + } + ], + "name": "Targets", + "custom": { + "showHeader": true, + "calc": "lastNotNull", + "displayMode": "labelValuesToRows", + "aggrDimension": "target" + }, + "options": { + "valueMappings": [], + "standardOptions": {} + }, + "overrides": [ + { + "properties": { + "valueMappings": [ + { + "type": "special", + "match": { + "special": 0 + }, + "result": { + "text": "UP", + "color": "#417505" + } + }, + { + "type": "range", + "match": { + "special": 1, + "from": 1 + }, + "result": { + "text": "DOWN", + "color": "#e90f0f" + } + } + ], + "standardOptions": {} + }, + "matcher": { + "value": "A" + } + } + ], + "version": "2.0.0", + "type": "table", + "layout": { + "h": 15, + "w": 24, + "x": 0, + "y": 1, + "i": "73c6eaf9-1685-4a7a-bf53-3d52afa1792e" + }, + "id": "73c6eaf9-1685-4a7a-bf53-3d52afa1792e" + } + ] + } +} \ No newline at end of file diff --git a/integrations/net_response/icon/tcp.png b/integrations/net_response/icon/tcp.png new file mode 100644 index 0000000000000000000000000000000000000000..6d663766cc9e09dc53b1f99ac2e284af7e833508 Binary files /dev/null and b/integrations/net_response/icon/tcp.png differ diff --git a/integrations/oracle/dashboards/oracle_by_categraf.json b/integrations/oracle/dashboards/oracle_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..2caa2a2a4d4faaa8d5b4ef936d2595b515c060de --- /dev/null +++ b/integrations/oracle/dashboards/oracle_by_categraf.json @@ -0,0 +1,824 @@ +{ + "name": "Oracle - 模板", + "tags": "Telegraf", + "ident": "", + "configs": { + "var": [ + { + "name": "ident", + "definition": "label_values(oracle_up,ident)", + "options": [ + "tt-fc-log00.nj" + ] + }, + { + "name": "instance", + "definition": "label_values(oracle_up{ident=\"$ident\"},instance)" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "16c3b81f-38ea-472e-ba9d-58f3218413c9", + "type": "row", + "name": "Activities", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "16c3b81f-38ea-472e-ba9d-58f3218413c9" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(oracle_activity_execute_count_value{ident=\"$ident\", instance=\"$instance\"}[2m])" + } + ], + "name": "execute count / second", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "decimals": 1 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "6834fcfd-6448-4848-9f63-72350d818a39" + }, + "id": "6834fcfd-6448-4848-9f63-72350d818a39" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(oracle_activity_user_commits_value{ident=\"$ident\", instance=\"$instance\"}[2m])" + } + ], + "name": "user commits / second", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "1754fda2-fa98-481e-ba86-520f1d7ebc0d" + }, + "id": "1754fda2-fa98-481e-ba86-520f1d7ebc0d" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(oracle_activity_user_rollbacks_value{ident=\"$ident\", instance=\"$instance\"}[2m])" + } + ], + "name": "user rollbacks / second", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "18357a10-cab4-4795-a4a4-fd960d37ce95" + }, + "id": "18357a10-cab4-4795-a4a4-fd960d37ce95" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_up{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "status", + "custom": { + "textMode": "value", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "special", + "match": { + "special": 1 + }, + "result": { + "text": "UP", + "color": "#5ea70f" + } + }, + { + "type": "special", + "match": { + "special": 0 + }, + "result": { + "text": "DOWN", + "color": "#f60f0f" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "8e5f9501-7bc0-4b77-9178-3ab875202f43" + }, + "id": "8e5f9501-7bc0-4b77-9178-3ab875202f43" + }, + { + "id": "aa019cdc-109a-4d3d-9549-9abc20720343", + "type": "row", + "name": "Waits", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "aa019cdc-109a-4d3d-9549-9abc20720343" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_wait_time_value{ident=\"$ident\", instance=\"$instance\"}", + "legend": "{{wait_class}}" + } + ], + "name": "Time waited", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 5, + "i": "51451443-eb34-4bdc-8fc5-1f0ee35eb73c" + }, + "id": "51451443-eb34-4bdc-8fc5-1f0ee35eb73c" + }, + { + "id": "108aa978-21be-45f4-92a6-f125f977965c", + "type": "row", + "name": "Tablespace", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "108aa978-21be-45f4-92a6-f125f977965c" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_tablespace_bytes{ident=\"$ident\", instance=\"$instance\"}/oracle_tablespace_max_bytes{ident=\"$ident\", instance=\"$instance\"}", + "legend": "{{tablespace}}" + } + ], + "name": "Used Percent", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percentUnit" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 13, + "i": "cf2454bd-0cf4-4f1a-a96a-b043db94da1f" + }, + "id": "cf2454bd-0cf4-4f1a-a96a-b043db94da1f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_tablespace_free{ident=\"$ident\", instance=\"$instance\"}", + "legend": "{{tablespace}}" + } + ], + "name": "Free space", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 13, + "i": "8f4aa32b-1ab6-49d2-94c5-83f408dd3cc4" + }, + "id": "8f4aa32b-1ab6-49d2-94c5-83f408dd3cc4" + }, + { + "id": "91c8d4ca-109e-4380-9222-92cffdcc5381", + "type": "row", + "name": "IO and TPS", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 20, + "i": "91c8d4ca-109e-4380-9222-92cffdcc5381" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_io_requests_per_second_value{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "IO Requests / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 21, + "i": "08552fed-10d2-4408-809e-eabc705db9f5" + }, + "id": "08552fed-10d2-4408-809e-eabc705db9f5" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_user_transaction_per_sec_value{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "TPS", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 21, + "i": "c42236d6-d18a-40bb-84dc-d287b1d0ac25" + }, + "id": "c42236d6-d18a-40bb-84dc-d287b1d0ac25" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_io_megabytes_per_second_value{ident=\"$ident\", instance=\"$instance\"}*1024*1024" + } + ], + "name": "IO Bytes / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 21, + "i": "3a564e64-4bed-4381-ab08-517b51f6cc66" + }, + "id": "3a564e64-4bed-4381-ab08-517b51f6cc66" + }, + { + "id": "34bc0a3c-23ee-4792-9552-0994fb027464", + "type": "row", + "name": "Connections", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 28, + "i": "34bc0a3c-23ee-4792-9552-0994fb027464" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sessions_value{ident=\"$ident\", instance=\"$instance\",status=\"ACTIVE\"}", + "legend": "" + } + ], + "name": "Sessions", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 29, + "i": "f46676da-b625-458e-b8d2-9079441ac3d6" + }, + "id": "f46676da-b625-458e-b8d2-9079441ac3d6" + }, + { + "id": "f8a61c95-0d00-4d38-a9d1-5813f70443da", + "type": "row", + "name": "Hit Ratio", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 36, + "i": "f8a61c95-0d00-4d38-a9d1-5813f70443da" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_buffer_cache_hit_ratio_value{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "Buffer Cache Hit Ratio", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 37, + "i": "f35e0768-204e-43c8-8d43-32f34a391bf8" + }, + "id": "f35e0768-204e-43c8-8d43-32f34a391bf8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_redo_allocation_hit_ratio_value{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "Redo Allocation Hit Ratio", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 37, + "i": "4239c9e1-0bf3-42ae-a7a5-8db2c38f1900" + }, + "id": "4239c9e1-0bf3-42ae-a7a5-8db2c38f1900" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_row_cache_hit_ratio_value{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "Row Cache Hit Ratio", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 37, + "i": "c40640c2-31b8-4bec-a88e-8a0f346da2a8" + }, + "id": "c40640c2-31b8-4bec-a88e-8a0f346da2a8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_library_cache_hit_ratio_value{ident=\"$ident\", instance=\"$instance\"}" + } + ], + "name": "Library Cache Hit Ratio", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 37, + "i": "93c7f8d2-093c-47fc-93e8-97b47bfcff80" + }, + "id": "93c7f8d2-093c-47fc-93e8-97b47bfcff80" + }, + { + "id": "9857bf37-1e40-4cf5-adbc-8331f5e128c8", + "type": "row", + "name": "Physical Read Write", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 44, + "i": "9857bf37-1e40-4cf5-adbc-8331f5e128c8" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_physical_read_bytes_per_sec_value{ident=\"$ident\", instance=\"$instance\"}" + }, + { + "expr": "oracle_sysmetric_Physical_Write_Bytes_Per_Sec{ident=\"$ident\", instance=\"$instance\"}", + "refId": "B" + } + ], + "name": "Physical Read Write Bytes / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 45, + "i": "b47bcfb9-2d26-454d-982a-039b769d405b" + }, + "id": "b47bcfb9-2d26-454d-982a-039b769d405b" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_physical_read_total_bytes_per_sec_value{ident=\"$ident\", instance=\"$instance\"}" + }, + { + "expr": "oracle_sysmetric_Physical_Write_Total_Bytes_Per_Sec{ident=\"$ident\", instance=\"$instance\"}", + "refId": "B" + } + ], + "name": "Physical Read Write Total Bytes / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 45, + "i": "0c4ea45a-913f-4464-9c31-eb026a365729" + }, + "id": "0c4ea45a-913f-4464-9c31-eb026a365729" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_physical_read_io_requests_per_sec_value{ident=\"$ident\", instance=\"$instance\"}" + }, + { + "expr": "oracle_sysmetric_Physical_Write_IO_Requests_Per_Sec{ident=\"$ident\", instance=\"$instance\"}", + "refId": "B" + } + ], + "name": "Physical RW IO Requests / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 45, + "i": "2bbfa751-4ac8-4ab9-affe-04b68e98daec" + }, + "id": "2bbfa751-4ac8-4ab9-affe-04b68e98daec" + }, + { + "targets": [ + { + "refId": "A", + "expr": "oracle_sysmetric_physical_read_total_io_requests_per_sec_value{ident=\"$ident\", instance=\"$instance\"}" + }, + { + "expr": "oracle_sysmetric_Physical_Write_Total_IO_Requests_Per_Sec{ident=\"$ident\", instance=\"$instance\"}", + "refId": "B" + } + ], + "name": "Physical RW Total IO Requests / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 45, + "i": "2ec5b041-dbd1-4013-bac8-bb0ac6fb5df6" + }, + "id": "2ec5b041-dbd1-4013-bac8-bb0ac6fb5df6" + } + ] + } +} \ No newline at end of file diff --git a/integrations/oracle/icon/oracle_logo.svg b/integrations/oracle/icon/oracle_logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..4183e0e5fb47407ded1cf6b1b16cba1c38d73fbc --- /dev/null +++ b/integrations/oracle/icon/oracle_logo.svg @@ -0,0 +1 @@ + diff --git a/docker/n9eetc/alerts/ping_by_categraf.json b/integrations/ping/alerts/ping_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/ping_by_categraf.json rename to integrations/ping/alerts/ping_by_categraf.json diff --git a/integrations/ping/dashboards/ping_by_categraf.json b/integrations/ping/dashboards/ping_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..56bd1ebe2abf7106550a7ed8b7d9dec37b97170e --- /dev/null +++ b/integrations/ping/dashboards/ping_by_categraf.json @@ -0,0 +1,96 @@ +{ + "name": "PING探测", + "tags": "", + "ident": "", + "configs": { + "version": "2.0.0", + "panels": [ + { + "id": "eb08a300-c59a-4b0d-8537-62512e833f48", + "type": "row", + "name": "Default chart group", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "eb08a300-c59a-4b0d-8537-62512e833f48" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "max(ping_result_code) by (target)", + "legend": "UP?" + }, + { + "expr": "max(ping_percent_packet_loss) by (target)", + "refId": "B", + "legend": "Packet Loss %" + }, + { + "expr": "max(httpresponse_response_time) by (target)", + "refId": "C", + "legend": "latency(s)" + } + ], + "name": "Ping", + "custom": { + "showHeader": true, + "calc": "lastNotNull", + "displayMode": "labelValuesToRows", + "aggrDimension": "target" + }, + "options": { + "valueMappings": [], + "standardOptions": {} + }, + "overrides": [ + { + "properties": { + "valueMappings": [ + { + "type": "special", + "match": { + "special": 0 + }, + "result": { + "text": "UP", + "color": "#417505" + } + }, + { + "type": "range", + "match": { + "special": 1, + "from": 1 + }, + "result": { + "text": "DOWN", + "color": "#e90f0f" + } + } + ], + "standardOptions": {} + }, + "matcher": { + "value": "A" + } + } + ], + "version": "2.0.0", + "type": "table", + "layout": { + "h": 15, + "w": 24, + "x": 0, + "y": 1, + "i": "1677138f-0f33-485c-8ee1-2db24cabbf54" + }, + "id": "1677138f-0f33-485c-8ee1-2db24cabbf54" + } + ] + } +} \ No newline at end of file diff --git a/integrations/ping/icon/ping.png b/integrations/ping/icon/ping.png new file mode 100644 index 0000000000000000000000000000000000000000..40ad2250dfd45bfe61164c41a8f85bde4883923f Binary files /dev/null and b/integrations/ping/icon/ping.png differ diff --git a/docker/n9eetc/alerts/process_by_exporter.json b/integrations/process/alerts/process_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/process_by_exporter.json rename to integrations/process/alerts/process_by_exporter.json diff --git a/docker/n9eetc/alerts/procstat_by_categraf.json b/integrations/process/alerts/procstat_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/procstat_by_categraf.json rename to integrations/process/alerts/procstat_by_categraf.json diff --git a/integrations/process/dashboards/process_by_exporter.json b/integrations/process/dashboards/process_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..71d1470f0c2cf2b637187f548897180634d8ea43 --- /dev/null +++ b/integrations/process/dashboards/process_by_exporter.json @@ -0,0 +1,1049 @@ +{ + "name": "Linux Process - 模板", + "tags": "Prometheus Process", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(namedprocess_namegroup_num_procs, instance)", + "multi": false, + "options": [ + "tt-fc-es02.nj:12346" + ] + }, + { + "definition": "label_values(namedprocess_namegroup_cpu_seconds_total{instance=~\"$instance\"},groupname)", + "name": "processes", + "multi": true, + "options": [ + "(sd-pam)", + "NetworkManager", + "YDLive", + "YDPython", + "YDService", + "agent", + "agetty", + "atd", + "auditd", + "barad_agent", + "bash", + "chronyd", + "crond", + "dbus-daemon", + "fc-agent", + "fc-alert", + "fc-checker", + "gpg-agent", + "less", + "lsmd", + "mongod", + "mysql", + "mysqld", + "nginx", + "ngo", + "node", + "openvpn", + "podman pause", + "polkitd", + "process-agent", + "redis-server", + "rsyslogd", + "sedispatch", + "sgagent", + "sh", + "ssh-agent", + "sshd", + "sssd", + "sssd_be", + "sssd_nss", + "su", + "sudo", + "systemd", + "systemd-journal", + "systemd-logind", + "systemd-udevd", + "tat_agent", + "trace-agent", + "tuned", + "unbound-anchor", + "vminsert-prod", + "vmselect-prod", + "vmstorage-prod" + ], + "allOption": true + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "021aedd5-dac7-4431-aeea-50c0c594e784", + "type": "row", + "name": "Cpu Usage", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "021aedd5-dac7-4431-aeea-50c0c594e784" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,(rate(namedprocess_namegroup_cpu_seconds_total{mode=\"user\",groupname=~\"$processes\",instance=~\"$instance\"}[5m]) + ignoring(mode) rate(namedprocess_namegroup_cpu_seconds_total{mode=\"system\",groupname=~\"$processes\",instance=~\"$instance\"}[5m])) or (irate(namedprocess_namegroup_cpu_seconds_total{mode=\"user\",groupname=~\"$processes\",instance=~\"$instance\"}[5m]) + ignoring(mode) irate(namedprocess_namegroup_cpu_seconds_total{mode=\"system\",groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Total CPU cores used", + "description": "进程占用CPU时间(用户态+内核态),倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percentUnit" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 1, + "i": "0f55cad5-ae67-44de-bba5-f6e1bd23066a" + }, + "id": "0f55cad5-ae67-44de-bba5-f6e1bd23066a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5, rate(namedprocess_namegroup_cpu_seconds_total{mode=\"system\",groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or ( irate(namedprocess_namegroup_cpu_seconds_total{mode=\"system\",groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by System CPU cores used", + "description": "进程占用CPU时间(内核态),倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percentUnit" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 1, + "i": "e200eb2a-9cd6-4954-880f-9a354c1af2cc" + }, + "id": "e200eb2a-9cd6-4954-880f-9a354c1af2cc" + }, + { + "id": "3f0a9068-51ab-4889-82e1-444cd23c56e8", + "type": "row", + "name": "Memory Usage", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 8, + "i": "3f0a9068-51ab-4889-82e1-444cd23c56e8" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"swapped\",instance=~\"$instance\"}[5m])+ ignoring (memtype) avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"resident\",instance=~\"$instance\"}[5m])) or (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"swapped\",instance=~\"$instance\"}[5m])+ ignoring (memtype) avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"resident\",instance=~\"$instance\"}[5m])) ))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Used memory", + "description": "进程常驻内存与交换空间平均占用容量之和,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 9, + "i": "9cb0cc2f-c122-4f5e-98ed-8bf310bd612a" + }, + "id": "9cb0cc2f-c122-4f5e-98ed-8bf310bd612a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5, (avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"resident\",instance=~\"$instance\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"resident\",instance=~\"$instance\"}[5m]) ))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Resident Memory", + "description": "进程常驻内存平均占用容量之和,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 9, + "i": "7aceae00-23be-447f-87df-47f74b278190" + }, + "id": "7aceae00-23be-447f-87df-47f74b278190" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"swapped\",instance=~\"$instance\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"swapped\",instance=~\"$instance\"}[5m]))) ", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Swapped Memory", + "description": "进程交换内存平均占用容量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 11, + "i": "55b255d8-756e-421c-9690-675d7c174e86" + }, + "id": "55b255d8-756e-421c-9690-675d7c174e86" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"virtual\",instance=~\"$instance\"}[5m]) or avg_over_time(namedprocess_namegroup_memory_bytes{groupname=~\"$processes\", memtype=\"virtual\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Virtual Memory", + "description": "进程虚拟内存平均占用容量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 11, + "i": "3c900819-5955-4bef-b419-ea8c47107b0d" + }, + "id": "3c900819-5955-4bef-b419-ea8c47107b0d" + }, + { + "id": "4d72a763-48f3-480b-9701-bc297be1bbfb", + "type": "row", + "name": "Disk IO Usage", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 18, + "i": "4d72a763-48f3-480b-9701-bc297be1bbfb" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,(rate(namedprocess_namegroup_write_bytes_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or irate(namedprocess_namegroup_write_bytes_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Bytes Written", + "description": "进程写数据量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesSI" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 19, + "i": "4cb30905-65f6-42aa-895e-ef3679905ef8" + }, + "id": "4cb30905-65f6-42aa-895e-ef3679905ef8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,(rate(namedprocess_namegroup_read_bytes_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or irate(namedprocess_namegroup_read_bytes_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Bytes Read", + "description": "进程读数据量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesSI" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 19, + "i": "d74f4d5f-db71-4781-b539-0df17dfcd42f" + }, + "id": "d74f4d5f-db71-4781-b539-0df17dfcd42f" + }, + { + "id": "e460097e-c3c8-45da-8202-dee1de996ddf", + "type": "row", + "name": "Process and Thread Counts", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 26, + "i": "e460097e-c3c8-45da-8202-dee1de996ddf" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,(max_over_time(namedprocess_namegroup_num_procs{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or max_over_time(namedprocess_namegroup_num_procs{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by number of processes instances", + "description": "同名进程数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 27, + "i": "1b44ce6f-ca65-49f5-892e-05444c9aa580" + }, + "id": "1b44ce6f-ca65-49f5-892e-05444c9aa580" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,(max_over_time(namedprocess_namegroup_num_threads{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or max_over_time(namedprocess_namegroup_num_threads{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by number of threads", + "description": "进程内线程数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 27, + "i": "24975aae-a573-4998-bdab-ffcb19cee2ae" + }, + "id": "24975aae-a573-4998-bdab-ffcb19cee2ae" + }, + { + "id": "3df9fc7b-bf9c-45bc-baa1-72ef83bc6b34", + "type": "row", + "name": "Context Switches", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 34, + "i": "3df9fc7b-bf9c-45bc-baa1-72ef83bc6b34" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( rate(namedprocess_namegroup_context_switches_total{groupname=~\"$processes\",instance=~\"$instance\",ctxswitchtype=\"voluntary\"}[5m]) or irate(namedprocess_namegroup_context_switches_total{groupname=~\"$processes\",instance=~\"$instance\",ctxswitchtype=\"voluntary\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top Processes by Voluntary Context Switches", + "description": "进程自愿中断(如I/O完成)次数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 35, + "i": "3775f2e7-fa98-4458-9659-0d1221b3cc03" + }, + "id": "3775f2e7-fa98-4458-9659-0d1221b3cc03" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( rate(namedprocess_namegroup_context_switches_total{groupname=~\"$processes\",instance=~\"$instance\",ctxswitchtype=\"nonvoluntary\"}[5m]) or irate(namedprocess_namegroup_context_switches_total{groupname=~\"$processes\",instance=~\"$instance\",ctxswitchtype=\"nonvoluntary\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top Processes by Non-Voluntary Context Switches", + "description": "进程被迫中断(如CPU时间耗尽)次数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 35, + "i": "a0e912d2-5b0d-42d0-bcf6-27bd54b5f07d" + }, + "id": "a0e912d2-5b0d-42d0-bcf6-27bd54b5f07d" + }, + { + "id": "79aedbba-da75-4393-b322-1565655017c0", + "type": "row", + "name": "File Descriptors", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 42, + "i": "79aedbba-da75-4393-b322-1565655017c0" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,(max_over_time(namedprocess_namegroup_open_filedesc{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or max_over_time(namedprocess_namegroup_open_filedesc{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Open File Descriptors", + "description": "打开文件数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 43, + "i": "73dc1195-8ffd-4c6e-b963-d10cde9ae7ba" + }, + "id": "73dc1195-8ffd-4c6e-b963-d10cde9ae7ba" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( max_over_time(namedprocess_namegroup_worst_fd_ratio{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or max_over_time(namedprocess_namegroup_worst_fd_ratio{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) ))*100", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by File Descriptor Usage Percent", + "description": "已打开文件数与允许打开文件数占比,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "percent" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 43, + "i": "5ffa8032-5b55-4841-a12e-81c1fc8443dd" + }, + "id": "5ffa8032-5b55-4841-a12e-81c1fc8443dd" + }, + { + "id": "fe967a6c-63bc-48d6-9632-07da1675581b", + "type": "row", + "name": "Page Faults", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 50, + "i": "fe967a6c-63bc-48d6-9632-07da1675581b" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( rate(namedprocess_namegroup_major_page_faults_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or irate(namedprocess_namegroup_major_page_faults_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Major Page Faults", + "description": "主要页缺失次数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 51, + "i": "8944f17d-51e6-435d-808a-8301b8539f08" + }, + "id": "8944f17d-51e6-435d-808a-8301b8539f08" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( rate(namedprocess_namegroup_minor_page_faults_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m]) or irate(namedprocess_namegroup_minor_page_faults_total{groupname=~\"$processes\",instance=~\"$instance\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top processes by Minor Page Faults", + "description": "次要页缺失次数,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 51, + "i": "b9bed634-d25a-4542-8fd9-4c63e9db766c" + }, + "id": "b9bed634-d25a-4542-8fd9-4c63e9db766c" + }, + { + "id": "76910d2f-d2c7-48c1-ad05-9651110f0ff1", + "type": "row", + "name": "Statuses", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 58, + "i": "76910d2f-d2c7-48c1-ad05-9651110f0ff1" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( max_over_time(namedprocess_namegroup_states{instance=~\"$instance\", groupname=~\"$processes\", state=\"Running\"}[5m]) or max_over_time(namedprocess_namegroup_states{instance=~\"$instance\", groupname=~\"$processes\", state=\"Running\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top running processes", + "description": "运行态同名进程数量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 59, + "i": "7c0a07a2-f936-41c1-a03e-328d8bc1feea" + }, + "id": "7c0a07a2-f936-41c1-a03e-328d8bc1feea" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,( max_over_time(namedprocess_namegroup_states{instance=~\"$instance\", groupname=~\"$processes\", state=\"Waiting\"}[5m]) or max_over_time(namedprocess_namegroup_states{instance=~\"$instance\", groupname=~\"$processes\", state=\"Waiting\"}[5m])))", + "legend": "{{groupname}}" + } + ], + "name": "Top of processes waiting on IO", + "description": "等待IO状态同名进程数量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 59, + "i": "31829466-def5-496f-83e8-10806b19410b" + }, + "id": "31829466-def5-496f-83e8-10806b19410b" + }, + { + "id": "9c79f06d-0262-4284-a65e-bcd0d84130be", + "type": "row", + "name": "Kernel Waits(WCHAN)", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 66, + "i": "9c79f06d-0262-4284-a65e-bcd0d84130be" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,sum(avg_over_time(namedprocess_namegroup_threads_wchan{instance=~\"$instance\", groupname=~\"$processes\"}[5m])) by (wchan) )", + "legend": "{{wchan}}" + } + ], + "name": "Kernel waits for All", + "description": "内核函数等待线程数量,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 67, + "i": "5decb873-5452-4084-8bbf-7bd3c9fa33f4" + }, + "id": "5decb873-5452-4084-8bbf-7bd3c9fa33f4" + }, + { + "targets": [ + { + "refId": "A", + "expr": "topk(5,sum(avg_over_time(namedprocess_namegroup_threads_wchan{instance=~\"$instance\", groupname=~\"$processes\"}[5m])) by (wchan,groupname) )", + "legend": "{{wchan}}" + } + ], + "name": "Kernel wait Details for All", + "description": "内核函数等待线程数量按进程统计,倒排前5", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 67, + "i": "20f14e96-5ac8-43f8-93a8-3f7696f5d8d5" + }, + "id": "20f14e96-5ac8-43f8-93a8-3f7696f5d8d5" + }, + { + "id": "972e68da-d03d-4638-81a2-fba73d215788", + "type": "row", + "name": "Uptime", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 74, + "i": "972e68da-d03d-4638-81a2-fba73d215788" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "time()-(namedprocess_namegroup_oldest_start_time_seconds{instance=~\"$instance\",groupname=~\"$processes\"}>0)", + "legend": "{{groupname}}" + } + ], + "name": "Processes by uptime", + "description": "进程启动时长", + "custom": { + "showHeader": true, + "calc": "lastNotNull", + "displayMode": "seriesToRows" + }, + "options": { + "standardOptions": { + "util": "seconds" + } + }, + "overrides": [ + { + "properties": { + "standardOptions": { + "util": "seconds" + }, + "valueMappings": [ + { + "type": "range", + "match": { + "to": 1800 + }, + "result": { + "color": "#f91010" + } + }, + { + "type": "range", + "match": { + "from": 1800 + }, + "result": { + "color": "#21f312" + } + } + ] + } + } + ], + "version": "2.0.0", + "type": "table", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 75, + "i": "eff1ee11-1d70-4c79-a159-588a363a2b60" + }, + "id": "eff1ee11-1d70-4c79-a159-588a363a2b60" + } + ] + } +} \ No newline at end of file diff --git a/integrations/process/icon/linux.png b/integrations/process/icon/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..906c61b38a6cdb4148eaf8fcfc657e76f89417ac Binary files /dev/null and b/integrations/process/icon/linux.png differ diff --git a/integrations/rabbixmq/dashboards/rabbitmq_by_categraf.json b/integrations/rabbixmq/dashboards/rabbitmq_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..2fd93c8880cf496c8a99ef5a00221ef470a08568 --- /dev/null +++ b/integrations/rabbixmq/dashboards/rabbitmq_by_categraf.json @@ -0,0 +1,1716 @@ +{ + "name": "RabbitMQ 3.8+", + "tags": "", + "ident": "", + "configs": { + "var": [ + { + "definition": "label_values(rabbitmq_identity_info, rabbitmq_cluster)", + "name": "rabbitmq_cluster" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "4466a232-248d-45a8-bf4d-05d5139c7346", + "type": "row", + "name": "Overview", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "4466a232-248d-45a8-bf4d-05d5139c7346" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_queue_messages_ready * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Ready messages", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 10000 + }, + "result": { + "color": "#4a90e2" + } + }, + { + "type": "range", + "match": { + "from": 100000 + }, + "result": { + "color": "#f50a0a" + } + }, + { + "type": "range", + "match": { + "to": 9999 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 7, + "x": 0, + "y": 1, + "i": "a20b5d06-4343-457a-89f2-33a52c4dec04" + }, + "id": "a20b5d06-4343-457a-89f2-33a52c4dec04" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Incoming messages / s", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 50 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 5, + "x": 7, + "y": 1, + "i": "893409b0-4ca0-450b-a0c9-f48eddf0e243" + }, + "id": "893409b0-4ca0-450b-a0c9-f48eddf0e243" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_channels * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) - sum(rabbitmq_channel_consumers * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Publishers", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 10 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 12, + "y": 1, + "i": "d596e7f0-5095-420e-bc38-674001dcf5f4" + }, + "id": "d596e7f0-5095-420e-bc38-674001dcf5f4" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_connections * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Connections", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 10 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 16, + "y": 1, + "i": "9f6d7dee-666d-4e1b-90d0-129b2e5ba085" + }, + "id": "9f6d7dee-666d-4e1b-90d0-129b2e5ba085" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_queues * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Queues", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 10 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 20, + "y": 1, + "i": "910eae0f-2b78-4d10-a780-8f997f6e96cb" + }, + "id": "910eae0f-2b78-4d10-a780-8f997f6e96cb" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_queue_messages_unacked * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Unacknowledged messages", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 99 + }, + "result": { + "color": "#417505" + } + }, + { + "type": "range", + "match": { + "from": 100 + }, + "result": { + "color": "#4a90e2" + } + }, + { + "type": "range", + "match": { + "from": 500 + }, + "result": { + "color": "#d0021b" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 7, + "x": 0, + "y": 2, + "i": "2e8cd60f-51b0-46b2-8c0f-bf55604d340d" + }, + "id": "2e8cd60f-51b0-46b2-8c0f-bf55604d340d" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_redelivered_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) +\nsum(rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) +\nsum(rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) +\nsum(rate(rabbitmq_channel_get_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) +\nsum(rate(rabbitmq_channel_get_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Outgoing messages / s", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 50 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 5, + "x": 7, + "y": 2, + "i": "4b242c1e-85d5-48b3-8cce-b467209245ec" + }, + "id": "4b242c1e-85d5-48b3-8cce-b467209245ec" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_channel_consumers * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Consumers", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 10 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 12, + "y": 2, + "i": "b87b1c6b-644a-42f4-915a-bd6857540f70" + }, + "id": "b87b1c6b-644a-42f4-915a-bd6857540f70" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_channels * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Channels", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "from": 10 + }, + "result": { + "color": "#417505" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 16, + "y": 2, + "i": "65103bcf-fb21-488d-a06b-7b0ea130ca4d" + }, + "id": "65103bcf-fb21-488d-a06b-7b0ea130ca4d" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_build_info * on(instance) group_left(rabbitmq_cluster) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Nodes", + "custom": { + "textMode": "valueAndName", + "colorMode": "background", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": null, + "from": 3 + }, + "result": { + "color": "#417505" + } + }, + { + "type": "range", + "match": { + "from": 8 + }, + "result": { + "color": "#e70909" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 4, + "x": 20, + "y": 2, + "i": "c6454712-e265-4387-ae86-9ac865af46f2" + }, + "id": "c6454712-e265-4387-ae86-9ac865af46f2" + }, + { + "id": "1712a96f-bcde-4d33-90b6-e2eba20527b9", + "type": "row", + "name": "Nodes", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 5, + "i": "1712a96f-bcde-4d33-90b6-e2eba20527b9" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "rabbitmq_build_info * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}" + } + ], + "name": "nodes", + "custom": { + "showHeader": true, + "calc": "lastNotNull", + "displayMode": "labelsOfSeriesToRows", + "columns": [ + "rabbitmq_cluster", + "rabbitmq_node", + "rabbitmq_version", + "erlang_version" + ] + }, + "options": { + "standardOptions": {} + }, + "overrides": [ + {} + ], + "version": "2.0.0", + "type": "table", + "layout": { + "h": 3, + "w": 24, + "x": 0, + "y": 6, + "i": "1b3fdea0-1921-48ae-b11f-31cc27f816b0" + }, + "id": "1b3fdea0-1921-48ae-b11f-31cc27f816b0" + }, + { + "targets": [ + { + "refId": "A", + "expr": "(rabbitmq_resident_memory_limit_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) -\n(rabbitmq_process_resident_memory_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "Memory available before publishers blocked", + "description": "If the value is zero or less, the memory alarm will be triggered and all publishing connections across all cluster nodes will be blocked.\n\nThis value can temporarily go negative because the memory alarm is triggered with a slight delay.\n\nThe kernel's view of the amount of memory used by the node can differ from what the node itself can observe. This means that this value can be negative for a sustained period of time.\n\nBy default nodes use resident set size (RSS) to compute how much memory they use. This strategy can be changed (see the guides below).\n\n* [Alarms](https://www.rabbitmq.com/alarms.html)\n* [Memory Alarms](https://www.rabbitmq.com/memory.html)\n* [Reasoning About Memory Use](https://www.rabbitmq.com/memory-use.html)\n* [Blocked Connection Notifications](https://www.rabbitmq.com/connection-blocked.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 7, + "i": "b91068ed-0914-4a8d-91dd-9ffb3b692516" + }, + "id": "b91068ed-0914-4a8d-91dd-9ffb3b692516" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rabbitmq_disk_space_available_bytes * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}" + } + ], + "name": "Disk space available before publishers blocked", + "description": "This metric is reported for the partition where the RabbitMQ data directory is stored.\n\nIf the value is zero or less, the disk alarm will be triggered and all publishing connections across all cluster nodes will be blocked.\n\nThis value can temporarily go negative because the free disk space alarm is triggered with a slight delay.\n\n* [Alarms](https://www.rabbitmq.com/alarms.html)\n* [Disk Space Alarms](https://www.rabbitmq.com/disk-alarms.html)\n* [Disk Space](https://www.rabbitmq.com/production-checklist.html#resource-limits-disk-space)\n* [Persistence Configuration](https://www.rabbitmq.com/persistence-conf.html)\n* [Blocked Connection Notifications](https://www.rabbitmq.com/connection-blocked.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 7, + "i": "a74ca489-6101-49bc-9560-4cde368bc47e" + }, + "id": "a74ca489-6101-49bc-9560-4cde368bc47e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "(rabbitmq_process_max_fds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) -\n(rabbitmq_process_open_fds * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "File descriptors available", + "description": "When this value reaches zero, new connections will not be accepted and disk write operations may fail.\n\nClient libraries, peer nodes and CLI tools will not be able to connect when the node runs out of available file descriptors.\n\n* [Open File Handles Limit](https://www.rabbitmq.com/production-checklist.html#resource-limits-file-handle-limit)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 8, + "x": 16, + "y": 7, + "i": "100ed9ae-2b84-4f2b-82b2-0444dd28deed" + }, + "id": "100ed9ae-2b84-4f2b-82b2-0444dd28deed" + }, + { + "targets": [ + { + "refId": "A", + "expr": "(rabbitmq_process_max_tcp_sockets * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) -\n(rabbitmq_process_open_tcp_sockets * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})" + } + ], + "name": "TCP sockets available", + "description": "When this value reaches zero, new connections will not be accepted.\n\nClient libraries, peer nodes and CLI tools will not be able to connect when the node runs out of available file descriptors.\n\n* [Networking and RabbitMQ](https://www.rabbitmq.com/networking.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 8, + "x": 16, + "y": 8, + "i": "a4859891-2538-47fc-946b-7ae3aa507c51" + }, + "id": "a4859891-2538-47fc-946b-7ae3aa507c51" + }, + { + "id": "fec98a71-0615-4782-bf8d-960529e243f9", + "type": "row", + "name": "QUEUED MESSAGES", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 14, + "i": "fec98a71-0615-4782-bf8d-960529e243f9" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_queue_messages_ready * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages ready to be delivered to consumers", + "description": "Total number of ready messages ready to be delivered to consumers.\n\nAim to keep this value as low as possible. RabbitMQ behaves best when messages are flowing through it. It's OK for publishers to occasionally outpace consumers, but the expectation is that consumers will eventually process all ready messages.\n\nIf this metric keeps increasing, your system will eventually run out of memory and/or disk space. Consider using TTL or Queue Length Limit to prevent unbounded message growth.\n\n* [Queues](https://www.rabbitmq.com/queues.html)\n* [Consumers](https://www.rabbitmq.com/consumers.html)\n* [Queue Length Limit](https://www.rabbitmq.com/maxlength.html)\n* [Time-To-Live and Expiration](https://www.rabbitmq.com/ttl.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 15, + "i": "145dc75a-d3b8-491f-9ba6-6da787c8e265" + }, + "id": "145dc75a-d3b8-491f-9ba6-6da787c8e265" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rabbitmq_queue_messages_unacked * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages pending consumer acknowledgement", + "description": "The total number of messages that are either in-flight to consumers, currently being processed by consumers or simply waiting for the consumer acknowledgements to be processed by the queue. Until the queue processes the message acknowledgement, the message will remain unacknowledged.\n\n* [Queues](https://www.rabbitmq.com/queues.html)\n* [Confirms and Acknowledgements](https://www.rabbitmq.com/confirms.html)\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 15, + "i": "a6ca328a-8b19-488e-a70d-74372f994901" + }, + "id": "a6ca328a-8b19-488e-a70d-74372f994901" + }, + { + "id": "500c51ed-a0d6-41d9-903f-d000e289dc2b", + "type": "row", + "name": "INCOMING MESSAGES", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 18, + "i": "500c51ed-a0d6-41d9-903f-d000e289dc2b" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages published / s", + "description": "The incoming message rate before any routing rules are applied.\n\nIf this value is lower than the number of messages published to queues, it may indicate that some messages are delivered to more than one queue.\n\nIf this value is higher than the number of messages published to queues, messages cannot be routed and will either be dropped or returned to publishers.\n\n* [Publishers](https://www.rabbitmq.com/publishers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 19, + "i": "ef4352a4-c281-4596-b89e-d79a565ca112" + }, + "id": "ef4352a4-c281-4596-b89e-d79a565ca112" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_confirmed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages confirmed to publishers / s", + "description": "The rate of messages confirmed by the broker to publishers. Publishers must opt-in to receive message confirmations.\n\nIf this metric is consistently at zero it may suggest that publisher confirms are not used by clients. The safety of published messages is likely to be at risk.\n\n* [Publisher Confirms](https://www.rabbitmq.com/confirms.html#publisher-confirms)\n* [Publisher Confirms and Data Safety](https://www.rabbitmq.com/publishers.html#data-safety)\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 19, + "i": "f0932549-d4a7-4eb1-a86e-35eb49569f29" + }, + "id": "f0932549-d4a7-4eb1-a86e-35eb49569f29" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_queue_messages_published_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages routed to queues / s", + "description": "The rate of messages received from publishers and successfully routed to the master queue replicas.\n\n* [Queues](https://www.rabbitmq.com/queues.html)\n* [Publishers](https://www.rabbitmq.com/publishers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 20, + "i": "f47766cb-3cdb-4f67-896f-77af421ff404" + }, + "id": "f47766cb-3cdb-4f67-896f-77af421ff404" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_unconfirmed[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages unconfirmed to publishers / s", + "description": "The rate of messages received from publishers that have publisher confirms enabled and the broker has not confirmed yet.\n\n* [Publishers](https://www.rabbitmq.com/publishers.html)\n* [Confirms and Acknowledgements](https://www.rabbitmq.com/confirms.html)\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 20, + "i": "644302ba-3c99-4787-8023-93770b0a9e6c" + }, + "id": "644302ba-3c99-4787-8023-93770b0a9e6c" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_unroutable_dropped_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Unroutable messages dropped / s", + "description": "The rate of messages that cannot be routed and are dropped. \n\nAny value above zero means message loss and likely suggests a routing problem on the publisher end.\n\n* [Unroutable Message Handling](https://www.rabbitmq.com/publishers.html#unroutable)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 21, + "i": "0a3eadfd-d84c-4070-9527-b4c62fc20787" + }, + "id": "0a3eadfd-d84c-4070-9527-b4c62fc20787" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_unroutable_returned_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Unroutable messages returned to publishers / s", + "description": "The rate of messages that cannot be routed and are returned back to publishers.\n\nSustained values above zero may indicate a routing problem on the publisher end.\n\n* [Unroutable Message Handling](https://www.rabbitmq.com/publishers.html#unroutable)\n* [When Will Published Messages Be Confirmed by the Broker?](https://www.rabbitmq.com/confirms.html#when-publishes-are-confirmed)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 21, + "i": "a14e8796-9614-4204-b5b8-5d1a47f356d8" + }, + "id": "a14e8796-9614-4204-b5b8-5d1a47f356d8" + }, + { + "id": "2405258c-c08f-4e49-960a-5c9a12b29f12", + "type": "row", + "name": "OUTGOING MESSAGES", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 24, + "i": "2405258c-c08f-4e49-960a-5c9a12b29f12" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(\n (rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) +\n (rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"})\n) by(rabbitmq_node)" + } + ], + "name": "Messages delivered / s", + "description": "The rate of messages delivered to consumers. It includes messages that have been redelivered.\n\nThis metric does not include messages that have been fetched by consumers using `basic.get` (consumed by polling).\n\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 25, + "i": "be0f1872-172c-4bc3-a901-4b645ebf5abe" + }, + "id": "be0f1872-172c-4bc3-a901-4b645ebf5abe" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_redelivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages redelivered / s", + "description": "The rate of messages that have been redelivered to consumers. It includes messages that have been requeued automatically and redelivered due to channel exceptions or connection closures.\n\nHaving some redeliveries is expected, but if this metric is consistently non-zero, it is worth investigating why.\n\n* [Negative Acknowledgement and Requeuing of Deliveries](https://www.rabbitmq.com/confirms.html#consumer-nacks-requeue)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 25, + "i": "eff794ca-e844-4a12-b230-690aadefa53f" + }, + "id": "eff794ca-e844-4a12-b230-690aadefa53f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_delivered_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages delivered with manual ack / s", + "description": "The rate of message deliveries to consumers that use manual acknowledgement mode.\n\nWhen this mode is used, RabbitMQ waits for consumers to acknowledge messages before more messages can be delivered.\n\nThis is the safest way of consuming messages.\n\n* [Consumer Acknowledgements](https://www.rabbitmq.com/confirms.html)\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 26, + "i": "2ed4be63-4fe9-462f-bc2f-967319bc3626" + }, + "id": "2ed4be63-4fe9-462f-bc2f-967319bc3626" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_delivered_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages delivered auto ack / s", + "description": "The rate of message deliveries to consumers that use automatic acknowledgement mode.\n\nWhen this mode is used, RabbitMQ does not wait for consumers to acknowledge message deliveries.\n\nThis mode is fire-and-forget and does not offer any delivery safety guarantees. It tends to provide higher throughput and it may lead to consumer overload and higher consumer memory usage.\n\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 26, + "i": "116c44e0-c8e1-4f02-8eae-2140997e2280" + }, + "id": "116c44e0-c8e1-4f02-8eae-2140997e2280" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_messages_acked_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Messages acknowledged / s", + "description": "The rate of message acknowledgements coming from consumers that use manual acknowledgement mode.\n\n* [Consumer Acknowledgements](https://www.rabbitmq.com/confirms.html)\n* [Consumer Prefetch](https://www.rabbitmq.com/consumer-prefetch.html)\n* [Consumer Acknowledgement Modes, Prefetch and Throughput](https://www.rabbitmq.com/confirms.html#channel-qos-prefetch-throughput)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 27, + "i": "a1184534-3226-4c9a-ba6c-6d5258998518" + }, + "id": "a1184534-3226-4c9a-ba6c-6d5258998518" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_get_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Polling operations with auto ack / s", + "description": "The rate of messages delivered to polling consumers that use automatic acknowledgement mode.\n\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\n\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 27, + "i": "2f34e8c7-e7fb-4695-afce-034a10081437" + }, + "id": "2f34e8c7-e7fb-4695-afce-034a10081437" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_get_empty_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Polling operations that yield no result / s", + "description": "The rate of polling consumer operations that yield no result.\n\nAny value above zero means that RabbitMQ resources are wasted by polling consumers.\n\nCompare this metric to the other polling consumer metrics to see the inefficiency rate.\n\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\n\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 28, + "i": "c7b1f4b8-5069-480f-a1f7-1c85dbc389c1" + }, + "id": "c7b1f4b8-5069-480f-a1f7-1c85dbc389c1" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channel_get_ack_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Polling operations with manual ack / s", + "description": "The rate of messages delivered to polling consumers that use manual acknowledgement mode.\n\nThe use of polling consumers is highly inefficient and therefore strongly discouraged.\n\n* [Fetching individual messages](https://www.rabbitmq.com/consumers.html#fetching)\n* [Consumers](https://www.rabbitmq.com/consumers.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 12, + "y": 28, + "i": "4c2f60db-3cb4-4926-944c-022cf876eec2" + }, + "id": "4c2f60db-3cb4-4926-944c-022cf876eec2" + }, + { + "id": "b6aa6f06-924f-4575-b4e0-d116ab744ea1", + "type": "row", + "name": "QUEUES", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 31, + "i": "b6aa6f06-924f-4575-b4e0-d116ab744ea1" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "rabbitmq_queues * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}" + } + ], + "name": "Total queues", + "description": "Total number of queue masters per node. \n\nThis metric makes it easy to see sub-optimal queue distribution in a cluster.\n\n* [Queue Masters, Data Locality](https://www.rabbitmq.com/ha.html#master-migration-data-locality)\n* [Queues](https://www.rabbitmq.com/queues.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 32, + "i": "c26434ca-065f-4088-81c6-ef8f0cbca552" + }, + "id": "c26434ca-065f-4088-81c6-ef8f0cbca552" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_queues_declared_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Queues declared / s", + "description": "The rate of queue declarations performed by clients.\n\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\n\n* [Queues](https://www.rabbitmq.com/queues.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 4, + "x": 12, + "y": 32, + "i": "ff021951-7991-4c3e-a667-8cd11e5c444c" + }, + "id": "ff021951-7991-4c3e-a667-8cd11e5c444c" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_queues_created_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Queues created / s", + "description": "The rate of new queues created (as opposed to redeclarations).\n\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\n\n* [Queues](https://www.rabbitmq.com/queues.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 4, + "x": 16, + "y": 32, + "i": "00bbb4fc-5cdf-4b29-a440-ff4da4325a0c" + }, + "id": "00bbb4fc-5cdf-4b29-a440-ff4da4325a0c" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_queues_deleted_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Queues deleted / s", + "description": "The rate of queues deleted.\n\nLow sustained values above zero are to be expected. High rates may be indicative of queue churn or high rates of connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\n\n* [Queues](https://www.rabbitmq.com/queues.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 4, + "x": 20, + "y": 32, + "i": "f802e41d-14fe-4193-a5cf-c31957b146f7" + }, + "id": "f802e41d-14fe-4193-a5cf-c31957b146f7" + }, + { + "id": "5c7acadf-f9ff-4db9-a284-a206be245733", + "type": "row", + "name": "CHANNELS", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 35, + "i": "5c7acadf-f9ff-4db9-a284-a206be245733" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "rabbitmq_channels * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}" + } + ], + "name": "Total channels", + "description": "Total number of channels on all currently opened connections.\n\nIf this metric grows monotonically it is highly likely a channel leak in one of the applications. Confirm channel leaks by using the _Channels opened_ and _Channels closed_ metrics.\n\n* [Channel Leak](https://www.rabbitmq.com/channels.html#channel-leaks)\n* [Channels](https://www.rabbitmq.com/channels.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 36, + "i": "362c622f-3fd8-4bdd-8dce-7ebf335f42f9" + }, + "id": "362c622f-3fd8-4bdd-8dce-7ebf335f42f9" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channels_opened_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Channels opened / s", + "description": "The rate of new channels opened by applications across all connections. Channels are expected to be long-lived.\n\nLow sustained values above zero are to be expected. High rates may be indicative of channel churn or mass connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\n\n* [High Channel Churn](https://www.rabbitmq.com/channels.html#high-channel-churn)\n* [Channels](https://www.rabbitmq.com/channels.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 36, + "i": "3a1a643b-8e2a-4ed1-8621-e5fae3ebc7c2" + }, + "id": "3a1a643b-8e2a-4ed1-8621-e5fae3ebc7c2" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_channels_closed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Channels closed / s", + "description": "The rate of channels closed by applications across all connections. Channels are expected to be long-lived.\n\nLow sustained values above zero are to be expected. High rates may be indicative of channel churn or mass connection recovery. Confirm connection recovery rates by using the _Connections opened_ metric.\n\n* [High Channel Churn](https://www.rabbitmq.com/channels.html#high-channel-churn)\n* [Channels](https://www.rabbitmq.com/channels.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 36, + "i": "01f1771a-dd54-45e5-aa1d-b6ee2111c53b" + }, + "id": "01f1771a-dd54-45e5-aa1d-b6ee2111c53b" + }, + { + "id": "419f22e8-08cc-4d91-8bc9-7c1055368146", + "type": "row", + "name": "CONNECTIONS", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 39, + "i": "419f22e8-08cc-4d91-8bc9-7c1055368146" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_connections_closed_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Connections closed / s", + "description": "The rate of connections closed. Connections are expected to be long-lived.\n\nLow sustained values above zero are to be expected. High rates may be indicative of connection churn or mass connection recovery.\n\n* [Connections](https://www.rabbitmq.com/connections.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 40, + "i": "c14fd6f4-d3de-4811-82c6-2b20c9146e89" + }, + "id": "c14fd6f4-d3de-4811-82c6-2b20c9146e89" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rabbitmq_connections * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}" + } + ], + "name": "Total connections", + "description": "Total number of client connections.\n\nIf this metric grows monotonically it is highly likely a connection leak in one of the applications. Confirm connection leaks by using the _Connections opened_ and _Connections closed_ metrics.\n\n* [Connection Leak](https://www.rabbitmq.com/connections.html#monitoring)\n* [Connections](https://www.rabbitmq.com/connections.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 12, + "x": 0, + "y": 40, + "i": "1d06679b-c603-4cbf-85a7-7c8f4594258f" + }, + "id": "1d06679b-c603-4cbf-85a7-7c8f4594258f" + }, + { + "targets": [ + { + "refId": "A", + "expr": "sum(rate(rabbitmq_connections_opened_total[60s]) * on(instance) group_left(rabbitmq_cluster, rabbitmq_node) rabbitmq_identity_info{rabbitmq_cluster=\"$rabbitmq_cluster\"}) by(rabbitmq_node)" + } + ], + "name": "Connections opened / s", + "description": "The rate of new connections opened by clients. Connections are expected to be long-lived.\n\nLow sustained values above zero are to be expected. High rates may be indicative of connection churn or mass connection recovery.\n\n* [Connection Leak](https://www.rabbitmq.com/connections.html#monitoring)\n* [Connections](https://www.rabbitmq.com/connections.html)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 40, + "i": "8c91f7fe-79e9-454a-9dd3-6f214a29e1eb" + }, + "id": "8c91f7fe-79e9-454a-9dd3-6f214a29e1eb" + } + ] + } +} \ No newline at end of file diff --git a/integrations/rabbixmq/icon/rabbitmq.png b/integrations/rabbixmq/icon/rabbitmq.png new file mode 100644 index 0000000000000000000000000000000000000000..206efd521e2561dd2805bc2f41dd5cc703325d98 Binary files /dev/null and b/integrations/rabbixmq/icon/rabbitmq.png differ diff --git a/docker/n9eetc/alerts/redis_by_categraf.json b/integrations/redis/alerts/redis_by_categraf.json similarity index 100% rename from docker/n9eetc/alerts/redis_by_categraf.json rename to integrations/redis/alerts/redis_by_categraf.json diff --git a/docker/n9eetc/alerts/redis_by_exporter.json b/integrations/redis/alerts/redis_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/redis_by_exporter.json rename to integrations/redis/alerts/redis_by_exporter.json diff --git a/integrations/redis/dashboards/redis_by_categraf.json b/integrations/redis/dashboards/redis_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..22424c59445f2179df6284d298173b0fcf598c6c --- /dev/null +++ b/integrations/redis/dashboards/redis_by_categraf.json @@ -0,0 +1,478 @@ +{ + "name": "Redis Overview - 模板 by categraf", + "tags": "Redis Prometheus", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(redis_uptime_in_seconds,instance)", + "selected": "10.206.0.16:6379" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "2ecb82c6-4d1a-41b5-8cdc-0284db16bd54", + "type": "row", + "name": "Basic Info", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "2ecb82c6-4d1a-41b5-8cdc-0284db16bd54" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "min(redis_uptime_in_seconds{instance=~\"$instance\"})" + } + ], + "name": "Redis Uptime", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "humantimeSeconds" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "b5acc352-a2bd-4afc-b6cd-d6db0905f807" + }, + "id": "b5acc352-a2bd-4afc-b6cd-d6db0905f807" + }, + { + "targets": [ + { + "expr": "sum(redis_connected_clients{instance=~\"$instance\"})" + } + ], + "name": "Connected Clients", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "8ccada5e-02f3-4efc-9b36-2a367612e4cb" + }, + "id": "8ccada5e-02f3-4efc-9b36-2a367612e4cb" + }, + { + "targets": [ + { + "expr": "redis_used_memory{instance=~\"$instance\"}" + } + ], + "name": "Memory Used", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 128000000 + }, + "result": { + "color": "#079e05" + } + }, + { + "type": "range", + "match": { + "from": 128000000 + }, + "result": { + "color": "#f10909" + } + } + ], + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "716dc7e7-c9ec-4195-93f6-db1c572ae8b0" + }, + "id": "716dc7e7-c9ec-4195-93f6-db1c572ae8b0" + }, + { + "targets": [ + { + "expr": "redis_maxmemory{instance=~\"$instance\"}" + } + ], + "name": "Max Memory Limit", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "bytesIEC" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "c6948161-db07-42df-beb1-765ee9c071a9" + }, + "id": "c6948161-db07-42df-beb1-765ee9c071a9" + }, + { + "id": "bd54cf4f-1abb-4945-8aab-f89aec16daef", + "type": "row", + "name": "Commands", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "bd54cf4f-1abb-4945-8aab-f89aec16daef" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "rate(redis_total_commands_processed{instance=~\"$instance\"}[5m])" + } + ], + "name": "Commands Executed / sec", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 5, + "i": "3d5f8c4e-0ddf-4d68-9f6d-2cc57d864a8e" + }, + "id": "3d5f8c4e-0ddf-4d68-9f6d-2cc57d864a8e" + }, + { + "targets": [ + { + "expr": "irate(redis_keyspace_hits{instance=~\"$instance\"}[5m])", + "legend": "hits" + }, + { + "expr": "irate(redis_keyspace_misses{instance=~\"$instance\"}[5m])", + "legend": "misses" + } + ], + "name": "Hits / Misses per Sec", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "noraml" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 5, + "i": "344a874d-c34d-4d2d-9bb4-46e0912cd9f5" + }, + "id": "344a874d-c34d-4d2d-9bb4-46e0912cd9f5" + }, + { + "targets": [ + { + "expr": "topk(5, irate(redis_cmdstat_calls{instance=~\"$instance\"} [1m]))", + "legend": "{{command}}" + } + ], + "name": "Top Commands", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 5, + "i": "3c83cd35-585c-4070-a210-1f17345f13f4" + }, + "id": "3c83cd35-585c-4070-a210-1f17345f13f4" + }, + { + "id": "1ea61073-a46d-4d7c-b072-fcdcbc5ac084", + "type": "row", + "name": "Keys", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "1ea61073-a46d-4d7c-b072-fcdcbc5ac084" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum (redis_keyspace_keys{instance=~\"$instance\"}) by (db)", + "legend": "{{db}}" + } + ], + "name": "Total Items per DB", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 13, + "i": "b2b4451c-4f8a-438a-8c48-69c95c68361e" + }, + "id": "b2b4451c-4f8a-438a-8c48-69c95c68361e" + }, + { + "targets": [ + { + "expr": "sum(rate(redis_expired_keys{instance=~\"$instance\"}[5m])) by (instance)", + "legend": "expired" + }, + { + "expr": "sum(rate(redis_evicted_keys{instance=~\"$instance\"}[5m])) by (instance)", + "legend": "evicted" + } + ], + "name": "Expired / Evicted", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 13, + "i": "894b9beb-e764-441c-ae04-13e5dbbb901d" + }, + "id": "894b9beb-e764-441c-ae04-13e5dbbb901d" + }, + { + "targets": [ + { + "expr": "sum(redis_keyspace_keys{instance=~\"$instance\"}) - sum(redis_keyspace_expires{instance=~\"$instance\"}) ", + "legend": "not expiring" + }, + { + "expr": "sum(redis_keyspace_expires{instance=~\"$instance\"}) ", + "legend": "expiring" + } + ], + "name": "Expiring vs Not-Expiring Keys", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "noraml" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 13, + "i": "f721a641-28c7-4e82-a37c-ec17704a0c57" + }, + "id": "f721a641-28c7-4e82-a37c-ec17704a0c57" + }, + { + "id": "60ff41ed-9d41-40ee-a13b-c968f3ca49d0", + "type": "row", + "name": "Network", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 20, + "i": "60ff41ed-9d41-40ee-a13b-c968f3ca49d0" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(rate(redis_total_net_input_bytes{instance=~\"$instance\"}[5m]))", + "legend": "input" + }, + { + "expr": "sum(rate(redis_total_net_output_bytes{instance=~\"$instance\"}[5m]))", + "legend": "output" + } + ], + "name": "Network I/O", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 21, + "i": "1841950c-e867-4a62-b846-78754dc0e34d" + }, + "id": "1841950c-e867-4a62-b846-78754dc0e34d" + } + ] + } +} \ No newline at end of file diff --git a/integrations/redis/dashboards/redis_by_exporter.json b/integrations/redis/dashboards/redis_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..a5ca18a341cc3ba0847775371406aebf19701824 --- /dev/null +++ b/integrations/redis/dashboards/redis_by_exporter.json @@ -0,0 +1,478 @@ +{ + "name": "Redis Overview - 模板 by exporter", + "tags": "Redis Prometheus", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(redis_uptime_in_seconds,instance)", + "selected": "10.206.0.16:6379" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "7e236455-0927-4695-8f19-3d911d0c83eb", + "type": "row", + "name": "Basic Info", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "7e236455-0927-4695-8f19-3d911d0c83eb" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "min(redis_uptime_in_seconds{instance=~\"$instance\"})" + } + ], + "name": "Redis Uptime", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "humantimeSeconds" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "d610dcec-a0f2-49dc-a368-a9fda3450f80" + }, + "id": "d610dcec-a0f2-49dc-a368-a9fda3450f80" + }, + { + "targets": [ + { + "expr": "sum(redis_connected_clients{instance=~\"$instance\"})" + } + ], + "name": "Connected Clients", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "339e6670-4597-4608-9f49-f7bdb243f7f1" + }, + "id": "339e6670-4597-4608-9f49-f7bdb243f7f1" + }, + { + "targets": [ + { + "expr": "redis_memory_used_bytes{instance=~\"$instance\"}" + } + ], + "name": "Memory Used", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 128000000 + }, + "result": { + "color": "#079e05" + } + }, + { + "type": "range", + "match": { + "from": 128000000 + }, + "result": { + "color": "#f10909" + } + } + ], + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "321099de-9061-4027-b77a-a44885c11ec3" + }, + "id": "321099de-9061-4027-b77a-a44885c11ec3" + }, + { + "targets": [ + { + "expr": "redis_memory_max_bytes{instance=~\"$instance\"}" + } + ], + "name": "Max Memory Limit", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "bytesIEC" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "3301dac6-df01-4373-84fb-b175ff2c7bfb" + }, + "id": "3301dac6-df01-4373-84fb-b175ff2c7bfb" + }, + { + "id": "631895f0-8eba-42da-a82b-203aacf71855", + "type": "row", + "name": "Commands", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "631895f0-8eba-42da-a82b-203aacf71855" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "rate(redis_commands_processed_total{instance=~\"$instance\"}[5m])" + } + ], + "name": "Commands Executed / sec", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 5, + "i": "02b519fd-3ddb-4a5e-b5f3-0ac00e1392e2" + }, + "id": "02b519fd-3ddb-4a5e-b5f3-0ac00e1392e2" + }, + { + "targets": [ + { + "expr": "irate(redis_keyspace_hits_total{instance=~\"$instance\"}[5m])", + "legend": "hits" + }, + { + "expr": "irate(redis_keyspace_misses_total{instance=~\"$instance\"}[5m])", + "legend": "misses" + } + ], + "name": "Hits / Misses per Sec", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "noraml" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 5, + "i": "e65b838d-e38d-42c0-80fb-fb7ecad37445" + }, + "id": "e65b838d-e38d-42c0-80fb-fb7ecad37445" + }, + { + "targets": [ + { + "expr": "topk(5, irate(redis_commands_total{instance=~\"$instance\"} [1m]))", + "legend": "{{cmd}}" + } + ], + "name": "Top Commands", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 5, + "i": "64301a42-adb8-4f12-9192-ae764f067305" + }, + "id": "64301a42-adb8-4f12-9192-ae764f067305" + }, + { + "id": "2f8d0391-ecd6-4c35-acd7-99a340fa64bd", + "type": "row", + "name": "Keys", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "2f8d0391-ecd6-4c35-acd7-99a340fa64bd" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum (redis_db_keys{instance=~\"$instance\"}) by (db)", + "legend": "{{db}}" + } + ], + "name": "Total Items per DB", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 13, + "i": "834d84f2-da11-49fc-8107-c253afcc4d67" + }, + "id": "834d84f2-da11-49fc-8107-c253afcc4d67" + }, + { + "targets": [ + { + "expr": "sum(rate(redis_expired_keys_total{instance=~\"$instance\"}[5m])) by (instance)", + "legend": "expired" + }, + { + "expr": "sum(rate(redis_evicted_keys_total{instance=~\"$instance\"}[5m])) by (instance)", + "legend": "evicted" + } + ], + "name": "Expired / Evicted", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 13, + "i": "e3503fcc-31c5-4f66-a31c-fbb421d03280" + }, + "id": "e3503fcc-31c5-4f66-a31c-fbb421d03280" + }, + { + "targets": [ + { + "expr": "sum(redis_db_keys{instance=~\"$instance\"}) - sum(redis_db_keys_expiring{instance=~\"$instance\"}) ", + "legend": "not expiring" + }, + { + "expr": "sum(redis_db_keys_expiring{instance=~\"$instance\"}) ", + "legend": "expiring" + } + ], + "name": "Expiring vs Not-Expiring Keys", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "noraml" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 13, + "i": "25daf589-9d61-476c-8f06-dd42a30f048d" + }, + "id": "25daf589-9d61-476c-8f06-dd42a30f048d" + }, + { + "id": "c86fdeb4-768c-4aa2-8a2c-204296316090", + "type": "row", + "name": "Network", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 20, + "i": "c86fdeb4-768c-4aa2-8a2c-204296316090" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "sum(rate(redis_net_input_bytes_total{instance=~\"$instance\"}[5m]))", + "legend": "input" + }, + { + "expr": "sum(rate(redis_net_output_bytes_total{instance=~\"$instance\"}[5m]))", + "legend": "output" + } + ], + "name": "Network I/O", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 21, + "i": "dd402e9d-7aff-4d8a-9e16-c338033d8a4d" + }, + "id": "dd402e9d-7aff-4d8a-9e16-c338033d8a4d" + } + ] + } +} \ No newline at end of file diff --git a/integrations/redis/icon/redis.png b/integrations/redis/icon/redis.png new file mode 100644 index 0000000000000000000000000000000000000000..12d3a1b25ff615829df8b80aca2d2aec6ea576d5 Binary files /dev/null and b/integrations/redis/icon/redis.png differ diff --git a/integrations/tomcat/dashboards/tomcat_by_categraf.json b/integrations/tomcat/dashboards/tomcat_by_categraf.json new file mode 100644 index 0000000000000000000000000000000000000000..5cb43388926609887731078b023738c937219523 --- /dev/null +++ b/integrations/tomcat/dashboards/tomcat_by_categraf.json @@ -0,0 +1,440 @@ +{ + "name": "Tomcat - 模板", + "tags": "Categraf", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(tomcat_up, instance)" + } + ], + "links": [ + { + "title": "n9e", + "url": "https://n9e.gitee.io/", + "targetBlank": true + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "cdc17d90-17f5-44b8-99a1-94764de61698", + "type": "row", + "name": "connector", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "cdc17d90-17f5-44b8-99a1-94764de61698" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(tomcat_connector_bytes_sent{instance=\"$instance\"}[1m])", + "legend": "sent" + }, + { + "expr": "rate(tomcat_connector_bytes_received{instance=\"$instance\"}[1m])", + "refId": "B", + "legend": "received" + } + ], + "name": "Traffic Bytes / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC" + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 1, + "i": "004b1408-dcfe-40e1-8910-a1f16a574a85" + }, + "id": "004b1408-dcfe-40e1-8910-a1f16a574a85" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(tomcat_connector_request_count{instance=\"$instance\"}[1m])", + "legend": "tomcat_connector_request_count" + }, + { + "expr": "rate(tomcat_connector_error_count{instance=\"$instance\"}[1m])", + "refId": "B", + "legend": "tomcat_connector_error_count" + } + ], + "name": "Request count / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 1, + "i": "010aabe0-9b60-4bf3-867e-b5773703090a" + }, + "id": "010aabe0-9b60-4bf3-867e-b5773703090a" + }, + { + "targets": [ + { + "refId": "A", + "expr": "tomcat_connector_max_threads{instance=\"$instance\"}", + "legend": "max_threads" + }, + { + "expr": "tomcat_connector_current_thread_count{instance=\"$instance\"}", + "refId": "B", + "legend": "current_thread_count" + }, + { + "expr": "tomcat_connector_current_threads_busy{instance=\"$instance\"}", + "refId": "C", + "legend": "current_threads_busy" + } + ], + "name": "Tread", + "description": "max_threads: The maximum number of allowed worker threads.\ncurrent_thread_count: The number of threads managed by the thread pool\ncurrent_threads_busy: The number of threads that are in use", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 3, + "i": "40ea7316-46b9-4447-8e45-6b5acb77f0d2" + }, + "id": "40ea7316-46b9-4447-8e45-6b5acb77f0d2" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(tomcat_connector_processing_time{instance=\"$instance\"}[1m])", + "legend": "{{name}}-processing_time" + } + ], + "name": "Processing time", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 3, + "i": "c99a33ea-53d1-464d-84aa-e7be9e15cfb1" + }, + "id": "c99a33ea-53d1-464d-84aa-e7be9e15cfb1" + }, + { + "id": "1cca4a8b-9352-4d15-9488-ae3aee7b17e3", + "type": "row", + "name": "mem used", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 10, + "i": "1cca4a8b-9352-4d15-9488-ae3aee7b17e3" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "tomcat_jvm_memory_max{instance=\"$instance\"}", + "legend": "max" + }, + { + "expr": "tomcat_jvm_memory_total{instance=\"$instance\"}", + "refId": "B", + "legend": "used" + }, + { + "expr": "tomcat_jvm_memory_free{instance=\"$instance\"}", + "refId": "C", + "legend": "free" + } + ], + "name": "Mem Used", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 24, + "x": 0, + "y": 11, + "i": "07059834-fb32-4dfd-88cd-a3bad0203c79" + }, + "id": "07059834-fb32-4dfd-88cd-a3bad0203c79" + }, + { + "id": "6e07a88a-41b4-4fe2-b11f-0f5c8cb95d52", + "type": "row", + "name": "memorypool", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 18, + "i": "6e07a88a-41b4-4fe2-b11f-0f5c8cb95d52" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "tomcat_jvm_memorypool_used{instance=\"$instance\"}", + "legend": "{{name}}" + } + ], + "name": "Used", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 19, + "i": "92d79cd5-7a53-4f29-a42a-34db741e3c62" + }, + "id": "92d79cd5-7a53-4f29-a42a-34db741e3c62" + }, + { + "targets": [ + { + "refId": "A", + "expr": "tomcat_jvm_memorypool_max{instance=\"$instance\"}", + "legend": "{{name}}" + } + ], + "name": "Max", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 19, + "i": "c6aace01-4b13-45dd-87e2-fd73cc3e5a28" + }, + "id": "c6aace01-4b13-45dd-87e2-fd73cc3e5a28" + }, + { + "targets": [ + { + "refId": "A", + "expr": "tomcat_jvm_memorypool_committed{instance=\"$instance\"}", + "legend": "{{name}}" + } + ], + "name": "Committed", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 19, + "i": "8cbc2b26-290e-4d11-a56c-e2f0645179a0" + }, + "id": "8cbc2b26-290e-4d11-a56c-e2f0645179a0" + }, + { + "targets": [ + { + "refId": "A", + "expr": "tomcat_jvm_memorypool_init{instance=\"$instance\"}", + "legend": "{{name}}" + } + ], + "name": "Init", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 19, + "i": "b81e4684-c03b-4ee1-86f2-45c883ace756" + }, + "id": "b81e4684-c03b-4ee1-86f2-45c883ace756" + } + ] + } +} \ No newline at end of file diff --git a/integrations/tomcat/icon/tomcat.png b/integrations/tomcat/icon/tomcat.png new file mode 100644 index 0000000000000000000000000000000000000000..da1478846ce35781432ab3b7a79610dd9e500705 Binary files /dev/null and b/integrations/tomcat/icon/tomcat.png differ diff --git a/etc/dashboards/vmware_by_vsphere-monitor.json b/integrations/vmware/dashboards/vmware_by_vsphere-monitor.json similarity index 100% rename from etc/dashboards/vmware_by_vsphere-monitor.json rename to integrations/vmware/dashboards/vmware_by_vsphere-monitor.json diff --git a/integrations/vmware/icon/vmware.png b/integrations/vmware/icon/vmware.png new file mode 100644 index 0000000000000000000000000000000000000000..24e9db5834cf63bcc052b01f6d34892b361dd92d Binary files /dev/null and b/integrations/vmware/icon/vmware.png differ diff --git a/docker/n9eetc/alerts/windows_by_exporter.json b/integrations/windows/alerts/windows_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/windows_by_exporter.json rename to integrations/windows/alerts/windows_by_exporter.json diff --git a/integrations/windows/dashboards/windows_by_exporter.json b/integrations/windows/dashboards/windows_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..dc2c5c1719533483d59d46299788a65b3748b4a3 --- /dev/null +++ b/integrations/windows/dashboards/windows_by_exporter.json @@ -0,0 +1,658 @@ +{ + "name": "Windows - 模板", + "tags": "Windows Prometheus", + "ident": "", + "configs": { + "var": [ + { + "name": "instance", + "definition": "label_values(windows_system_system_up_time, instance)" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "13fc4558-3a83-4165-bf93-bc4eaea0f097", + "type": "row", + "name": "Basic Info", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "13fc4558-3a83-4165-bf93-bc4eaea0f097" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "time() - windows_system_system_up_time{instance=~\"$instance\"}", + "legend": "" + } + ], + "name": "Uptime", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "humantimeSeconds" + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "666cda14-4732-4f80-a024-675e2d244051" + }, + "id": "666cda14-4732-4f80-a024-675e2d244051" + }, + { + "targets": [ + { + "expr": "windows_cs_logical_processors{instance=~\"$instance\"}", + "legend": "" + } + ], + "name": "CPU Core Total", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "164edb5c-d8f3-4b77-8af3-7907b24a1073" + }, + "id": "164edb5c-d8f3-4b77-8af3-7907b24a1073" + }, + { + "targets": [ + { + "expr": "windows_cs_physical_memory_bytes{instance=~\"$instance\"}" + } + ], + "name": "Memory Total", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + } + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "e9ddf0d3-53e5-43a8-83c7-10aa9d0028ad" + }, + "id": "e9ddf0d3-53e5-43a8-83c7-10aa9d0028ad" + }, + { + "targets": [ + { + "expr": "windows_os_processes{instance=~\"$instance\"}" + } + ], + "name": "Process Total", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": {} + }, + "options": { + "valueMappings": [ + { + "type": "range", + "match": { + "to": 100 + }, + "result": { + "color": "#109d06" + } + }, + { + "type": "range", + "match": { + "from": 100 + }, + "result": { + "color": "#d11010" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "288ae77f-aa29-427c-932a-d0445e7d749e" + }, + "id": "288ae77f-aa29-427c-932a-d0445e7d749e" + }, + { + "id": "a0248950-a7c4-47f2-9e75-27666ef428cd", + "type": "row", + "name": "CPU Memory Disk", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 4, + "i": "a0248950-a7c4-47f2-9e75-27666ef428cd" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "100 * sum by (instance) (rate(windows_cpu_time_total{mode != 'idle'}[5m])) / count by (instance) (windows_cpu_core_frequency_mhz) ", + "legend": "CPU Util" + } + ], + "name": "Cpu Util", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 5, + "i": "b1c60c40-94ba-4b76-a688-1532e26d3a52" + }, + "id": "b1c60c40-94ba-4b76-a688-1532e26d3a52" + }, + { + "targets": [ + { + "expr": "100 - (windows_os_physical_memory_free_bytes{instance=~\"$instance\"} / windows_cs_physical_memory_bytes{instance=~\"$instance\"})*100" + } + ], + "name": "Memory Util", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 2 + }, + "thresholds": { + "steps": [ + { + "value": 70, + "color": "#e71313" + } + ] + } + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 5, + "i": "799d9f5c-450a-4db3-80d0-71a64c6d8d73" + }, + "id": "799d9f5c-450a-4db3-80d0-71a64c6d8d73" + }, + { + "targets": [ + { + "expr": "100 - (windows_logical_disk_free_bytes{instance=~\"$instance\"} / windows_logical_disk_size_bytes{instance=~\"$instance\"})*100", + "legend": "{{volume}}" + } + ], + "name": "Disk Util", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 5, + "i": "1dae2abc-d7eb-47b9-8280-fcc1810803cb" + }, + "id": "1dae2abc-d7eb-47b9-8280-fcc1810803cb" + }, + { + "targets": [ + { + "expr": "windows_logical_disk_free_bytes{instance=~\"$instance\"}", + "legend": "{{volume}} Free" + }, + { + "expr": "windows_logical_disk_size_bytes{instance=~\"$instance\"}", + "legend": "{{volume}} Total" + } + ], + "name": "Disk Free", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 0 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 5, + "i": "fd93766b-1099-4791-ace1-2648a38a23fb" + }, + "id": "fd93766b-1099-4791-ace1-2648a38a23fb" + }, + { + "id": "47cfd14a-7c12-4d42-aa98-c768633bb1b9", + "type": "row", + "name": "Disk IO", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 12, + "i": "47cfd14a-7c12-4d42-aa98-c768633bb1b9" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "irate(windows_logical_disk_read_bytes_total{instance=~\"$instance\"}[5m])", + "legend": "{{volume}} Read" + }, + { + "expr": "irate(windows_logical_disk_write_bytes_total{instance=~\"$instance\"}[5m])", + "legend": "{{volume}} Write" + } + ], + "name": "Read/Write Bytes / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bytesIEC", + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 13, + "i": "1105b0bf-476d-428c-b01b-c7fef29ee5c2" + }, + "id": "1105b0bf-476d-428c-b01b-c7fef29ee5c2" + }, + { + "targets": [ + { + "expr": "irate(windows_logical_disk_reads_total{instance=~\"$instance\"}[5m])", + "legend": "{{volume}} Read" + }, + { + "expr": "irate(windows_logical_disk_writes_total{instance=~\"$instance\"}[5m])", + "legend": "{{volume}} Write" + } + ], + "name": "Read/Write / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 2 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 13, + "i": "470503f0-e414-48ec-88bf-1d5c885960d0" + }, + "id": "470503f0-e414-48ec-88bf-1d5c885960d0" + }, + { + "id": "22ffcddd-74d1-4db3-bfa6-b5fecbf99c6e", + "type": "row", + "name": "Network", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 20, + "i": "22ffcddd-74d1-4db3-bfa6-b5fecbf99c6e" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "irate(windows_net_bytes_sent_total{instance=~\"$instance\",nic!~'isatap.*|VPN.*'}[5m])*8", + "legend": "{{nic}} Sent" + }, + { + "expr": "irate(windows_net_bytes_received_total{instance=~\"$instance\",nic!~'isatap.*|VPN.*'}[5m])*8", + "legend": "{{nic}} Received" + } + ], + "name": "Sent/Received bits / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "util": "bitsIEC", + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 0, + "y": 21, + "i": "6a2d168f-c316-4e6f-b9b4-d91a0de6ea10" + }, + "id": "6a2d168f-c316-4e6f-b9b4-d91a0de6ea10" + }, + { + "targets": [ + { + "expr": "(irate(windows_net_bytes_total{instance=~\"$instance\",nic!~'isatap.*|VPN.*'}[5m]) * 8 / windows_net_current_bandwidth{instance=~\"$instance\",nic!~'isatap.*|VPN.*'}) * 100" + } + ], + "name": "Network Util", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": { + "decimals": 1 + }, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 8, + "y": 21, + "i": "befa8f81-2ae5-4b93-8883-057a9bff79a8" + }, + "id": "befa8f81-2ae5-4b93-8883-057a9bff79a8" + }, + { + "targets": [ + { + "expr": "irate(windows_net_packets_outbound_discarded{instance=~\"$instance\", nic!~'isatap.*|VPN.*'}[5m]) + irate(windows_net_packets_outbound_errors{instance=~\"$instance\"}[5m])", + "legend": "outbound" + }, + { + "expr": "irate(windows_net_packets_received_discarded{job=~\"$job\",instance=~\"$instance\", nic!~'isatap.*|VPN.*'}[5m]) + irate(windows_net_packets_received_errors{job=~\"$job\",instance=~\"$instance\"}[5m])", + "legend": "received" + } + ], + "name": "Packets / Second", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 8, + "x": 16, + "y": 21, + "i": "f710ea1b-c8b3-4ca1-a8fc-4d2a8b21895d" + }, + "id": "f710ea1b-c8b3-4ca1-a8fc-4d2a8b21895d" + }, + { + "id": "0a9d73c2-caff-4ae9-8159-2bc96dd847fb", + "type": "row", + "name": "System", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 28, + "i": "0a9d73c2-caff-4ae9-8159-2bc96dd847fb" + }, + "collapsed": true + }, + { + "targets": [ + { + "expr": "windows_system_threads{instance=~\"$instance\"}" + } + ], + "name": "System Threads", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 29, + "i": "7d995748-cf74-4ae7-9ad4-dab4eefd84f9" + }, + "id": "7d995748-cf74-4ae7-9ad4-dab4eefd84f9" + }, + { + "targets": [ + { + "expr": "irate(windows_system_exception_dispatches_total{instance=~\"$instance\"}[5m])" + } + ], + "name": "System exception dispatches", + "options": { + "tooltip": { + "mode": "all", + "sort": "desc" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 29, + "i": "4cd91349-03ea-47b0-8c13-80191fc80e02" + }, + "id": "4cd91349-03ea-47b0-8c13-80191fc80e02" + } + ] + } +} \ No newline at end of file diff --git a/integrations/windows/icon/windows.png b/integrations/windows/icon/windows.png new file mode 100644 index 0000000000000000000000000000000000000000..f81d64e1627f54bad26f0e3693285ae4d1604271 Binary files /dev/null and b/integrations/windows/icon/windows.png differ diff --git a/docker/n9eetc/alerts/zookeeper_by_exporter.json b/integrations/zookeeper/alerts/zookeeper_by_exporter.json similarity index 100% rename from docker/n9eetc/alerts/zookeeper_by_exporter.json rename to integrations/zookeeper/alerts/zookeeper_by_exporter.json diff --git a/integrations/zookeeper/dashboards/zookeeper_by_exporter.json b/integrations/zookeeper/dashboards/zookeeper_by_exporter.json new file mode 100644 index 0000000000000000000000000000000000000000..678f28d17d3bd0428c76df7e0c7fbec88e16d49e --- /dev/null +++ b/integrations/zookeeper/dashboards/zookeeper_by_exporter.json @@ -0,0 +1,425 @@ +{ + "name": "Zookeeper - 模板", + "tags": "", + "ident": "", + "configs": { + "var": [ + { + "name": "job", + "definition": "label_values(zk_up,job)" + }, + { + "definition": "label_values(zk_up,instance)", + "name": "instance" + } + ], + "version": "2.0.0", + "panels": [ + { + "id": "2718a256-a74a-4661-ae74-fe21d765c8b4", + "type": "row", + "name": "overview", + "layout": { + "h": 1, + "w": 24, + "x": 0, + "y": 0, + "i": "2718a256-a74a-4661-ae74-fe21d765c8b4" + }, + "collapsed": true + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_up{job=\"$job\", instance=\"$instance\"}", + "legend": "up" + } + ], + "name": "up", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 40 + } + }, + "options": { + "valueMappings": [ + { + "type": "special", + "match": { + "special": 1 + }, + "result": { + "color": "#3d950e", + "text": "UP" + } + }, + { + "type": "special", + "match": { + "special": 0 + }, + "result": { + "color": "#f01414", + "text": "DOWN" + } + } + ], + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 0, + "y": 1, + "i": "4474ec3c-360b-4b3c-ab16-978305ecc438" + }, + "id": "4474ec3c-360b-4b3c-ab16-978305ecc438" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_znode_count{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}" + } + ], + "name": "zk_znode_count", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 50 + } + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 6, + "y": 1, + "i": "5ef1653d-ca20-47b9-9604-f3a0dfffbdd6" + }, + "id": "5ef1653d-ca20-47b9-9604-f3a0dfffbdd6" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_watch_count{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}" + } + ], + "name": "zk_watch_count", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 50 + } + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 12, + "y": 1, + "i": "fef22c82-540a-4662-9913-26b6b38e8aa3" + }, + "id": "fef22c82-540a-4662-9913-26b6b38e8aa3" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_ephemerals_count{job=~\"$job\", instance=~\"$instance\"}", + "legend": "zk_ephemerals_count" + } + ], + "name": "zk_ephemerals_count", + "custom": { + "textMode": "value", + "colorMode": "value", + "calc": "lastNotNull", + "colSpan": 1, + "textSize": { + "value": 50 + } + }, + "options": { + "standardOptions": {} + }, + "version": "2.0.0", + "type": "stat", + "layout": { + "h": 3, + "w": 6, + "x": 18, + "y": 1, + "i": "12510246-7469-4868-9dad-8d761f574ad3" + }, + "id": "12510246-7469-4868-9dad-8d761f574ad3" + }, + { + "targets": [ + { + "refId": "A", + "expr": "rate(zk_packets_sent{job=~\"$job\", instance=~\"$instance\"}[5m])", + "legend": "{{instance}}-sent" + }, + { + "expr": "rate(zk_packets_received{job=~\"$job\", instance=~\"$instance\"}[5m])", + "refId": "B", + "legend": "{{instance}}-received" + } + ], + "name": "Pakages", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 0, + "y": 2, + "i": "0f7b7057-8446-49b8-ab45-beb9fb2a6af3" + }, + "id": "0f7b7057-8446-49b8-ab45-beb9fb2a6af3" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_num_alive_connections{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}" + } + ], + "name": "alive_connections", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 6, + "y": 4, + "i": "3e2d6853-4e2b-4b71-8601-fd2ececceb30" + }, + "id": "3e2d6853-4e2b-4b71-8601-fd2ececceb30" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_open_file_descriptor_count{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}-open" + }, + { + "expr": "zk_max_file_descriptor_count{job=~\"$job\", instance=~\"$instance\"}", + "refId": "B", + "legend": "{{instance}}-max" + } + ], + "name": "file_descriptor", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 12, + "y": 4, + "i": "83205acd-35b8-404b-9883-cf3f656b022b" + }, + "id": "83205acd-35b8-404b-9883-cf3f656b022b" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_avg_latency{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}-avg" + }, + { + "expr": "zk_min_latency{job=~\"$job\", instance=~\"$instance\"}", + "refId": "B", + "legend": "{{instance}}-min" + }, + { + "expr": "zk_max_latency{job=~\"$job\", instance=~\"$instance\"}", + "refId": "C", + "legend": "{{instance}}-max" + } + ], + "name": "latency(ms)", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 18, + "y": 4, + "i": "ea71f66f-690f-4e4e-95bc-b835f0d6027e" + }, + "id": "ea71f66f-690f-4e4e-95bc-b835f0d6027e" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_outstanding_requests{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}" + } + ], + "name": "outstanding_requests", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 6, + "x": 0, + "y": 4, + "i": "906d651b-234b-4e38-b90f-7ac31b267eb8" + }, + "id": "906d651b-234b-4e38-b90f-7ac31b267eb8" + }, + { + "targets": [ + { + "refId": "A", + "expr": "zk_approximate_data_size{job=~\"$job\", instance=~\"$instance\"}", + "legend": "{{instance}}" + } + ], + "name": "approximate_data_size", + "options": { + "tooltip": { + "mode": "all", + "sort": "none" + }, + "legend": { + "displayMode": "hidden" + }, + "standardOptions": {}, + "thresholds": {} + }, + "custom": { + "drawStyle": "lines", + "lineInterpolation": "smooth", + "fillOpacity": 0.5, + "stack": "off" + }, + "version": "2.0.0", + "type": "timeseries", + "layout": { + "h": 7, + "w": 12, + "x": 12, + "y": 2, + "i": "83bb38e0-0074-4a80-ae2a-ea242db0da7b" + }, + "id": "83bb38e0-0074-4a80-ae2a-ea242db0da7b" + } + ] + } +} \ No newline at end of file diff --git a/integrations/zookeeper/icon/zookeeper.png b/integrations/zookeeper/icon/zookeeper.png new file mode 100644 index 0000000000000000000000000000000000000000..a0f734eae074d1784f74a67a25ac1b7ea48a5ede Binary files /dev/null and b/integrations/zookeeper/icon/zookeeper.png differ diff --git a/src/server/memsto/alert_mute_cache.go b/memsto/alert_mute_cache.go similarity index 65% rename from src/server/memsto/alert_mute_cache.go rename to memsto/alert_mute_cache.go index c4d259d348f2583e281f734adb60df69059904a1..286c6f26e94b6d9d09a7a15ed5fd9780e3cb5e54 100644 --- a/src/server/memsto/alert_mute_cache.go +++ b/memsto/alert_mute_cache.go @@ -5,25 +5,33 @@ import ( "sync" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) type AlertMuteCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex mutes map[int64][]*models.AlertMute // key: busi_group_id } -var AlertMuteCache = AlertMuteCacheType{ - statTotal: -1, - statLastUpdated: -1, - mutes: make(map[int64][]*models.AlertMute), +func NewAlertMuteCache(ctx *ctx.Context, stats *Stats) *AlertMuteCacheType { + amc := &AlertMuteCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + mutes: make(map[int64][]*models.AlertMute), + } + amc.SyncAlertMutes() + return amc } func (amc *AlertMuteCacheType) Reset() { @@ -75,48 +83,47 @@ func (amc *AlertMuteCacheType) GetAllStructs() map[int64][]models.AlertMute { return ret } -func SyncAlertMutes() { - err := syncAlertMutes() +func (amc *AlertMuteCacheType) SyncAlertMutes() { + err := amc.syncAlertMutes() if err != nil { fmt.Println("failed to sync alert mutes:", err) exit(1) } - go loopSyncAlertMutes() + go amc.loopSyncAlertMutes() } -func loopSyncAlertMutes() { +func (amc *AlertMuteCacheType) loopSyncAlertMutes() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncAlertMutes(); err != nil { + if err := amc.syncAlertMutes(); err != nil { logger.Warning("failed to sync alert mutes:", err) } } } -func syncAlertMutes() error { +func (amc *AlertMuteCacheType) syncAlertMutes() error { start := time.Now() - stat, err := models.AlertMuteStatistics("") + stat, err := models.AlertMuteStatistics(amc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec AlertMuteStatistics") } - if !AlertMuteCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_alert_mutes").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_alert_mutes").Set(0) + if !amc.StatChanged(stat.Total, stat.LastUpdated) { + amc.stats.GaugeCronDuration.WithLabelValues("sync_alert_mutes").Set(0) + amc.stats.GaugeSyncNumber.WithLabelValues("sync_alert_mutes").Set(0) logger.Debug("alert mutes not changed") return nil } - lst, err := models.AlertMuteGetsByCluster("") + lst, err := models.AlertMuteGetsAll(amc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec AlertMuteGetsByCluster") } oks := make(map[int64][]*models.AlertMute) - for i := 0; i < len(lst); i++ { err = lst[i].Parse() if err != nil { @@ -127,11 +134,11 @@ func syncAlertMutes() error { oks[lst[i].GroupId] = append(oks[lst[i].GroupId], lst[i]) } - AlertMuteCache.Set(oks, stat.Total, stat.LastUpdated) + amc.Set(oks, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_alert_mutes").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_alert_mutes").Set(float64(len(lst))) + amc.stats.GaugeCronDuration.WithLabelValues("sync_alert_mutes").Set(float64(ms)) + amc.stats.GaugeSyncNumber.WithLabelValues("sync_alert_mutes").Set(float64(len(lst))) logger.Infof("timer: sync mutes done, cost: %dms, number: %d", ms, len(lst)) return nil diff --git a/src/server/memsto/alert_rule_cache.go b/memsto/alert_rule_cache.go similarity index 61% rename from src/server/memsto/alert_rule_cache.go rename to memsto/alert_rule_cache.go index a9142952f9de66747e791b6cf278ad5f935f8112..368858b8a1a32d60a9e75601e8ceaf9fb71aeff9 100644 --- a/src/server/memsto/alert_rule_cache.go +++ b/memsto/alert_rule_cache.go @@ -5,25 +5,33 @@ import ( "sync" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) type AlertRuleCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex rules map[int64]*models.AlertRule // key: rule id } -var AlertRuleCache = AlertRuleCacheType{ - statTotal: -1, - statLastUpdated: -1, - rules: make(map[int64]*models.AlertRule), +func NewAlertRuleCache(ctx *ctx.Context, stats *Stats) *AlertRuleCacheType { + arc := &AlertRuleCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + rules: make(map[int64]*models.AlertRule), + } + arc.SyncAlertRules() + return arc } func (arc *AlertRuleCacheType) Reset() { @@ -72,41 +80,41 @@ func (arc *AlertRuleCacheType) GetRuleIds() []int64 { return list } -func SyncAlertRules() { - err := syncAlertRules() +func (arc *AlertRuleCacheType) SyncAlertRules() { + err := arc.syncAlertRules() if err != nil { fmt.Println("failed to sync alert rules:", err) exit(1) } - go loopSyncAlertRules() + go arc.loopSyncAlertRules() } -func loopSyncAlertRules() { +func (arc *AlertRuleCacheType) loopSyncAlertRules() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncAlertRules(); err != nil { + if err := arc.syncAlertRules(); err != nil { logger.Warning("failed to sync alert rules:", err) } } } -func syncAlertRules() error { +func (arc *AlertRuleCacheType) syncAlertRules() error { start := time.Now() - stat, err := models.AlertRuleStatistics("") + stat, err := models.AlertRuleStatistics(arc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec AlertRuleStatistics") } - if !AlertRuleCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_alert_rules").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_alert_rules").Set(0) + if !arc.StatChanged(stat.Total, stat.LastUpdated) { + arc.stats.GaugeCronDuration.WithLabelValues("sync_alert_rules").Set(0) + arc.stats.GaugeSyncNumber.WithLabelValues("sync_alert_rules").Set(0) logger.Debug("alert rules not changed") return nil } - lst, err := models.AlertRuleGetsByCluster("") + lst, err := models.AlertRuleGetsAll(arc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec AlertRuleGetsByCluster") } @@ -116,11 +124,11 @@ func syncAlertRules() error { m[lst[i].Id] = lst[i] } - AlertRuleCache.Set(m, stat.Total, stat.LastUpdated) + arc.Set(m, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_alert_rules").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_alert_rules").Set(float64(len(m))) + arc.stats.GaugeCronDuration.WithLabelValues("sync_alert_rules").Set(float64(ms)) + arc.stats.GaugeSyncNumber.WithLabelValues("sync_alert_rules").Set(float64(len(m))) logger.Infof("timer: sync rules done, cost: %dms, number: %d", ms, len(m)) return nil diff --git a/src/server/memsto/alert_subsribe_cache.go b/memsto/alert_subscribe_cache.go similarity index 59% rename from src/server/memsto/alert_subsribe_cache.go rename to memsto/alert_subscribe_cache.go index 4ada14edc4ac4336979db07b84ef38a7ccab70b1..65258318e0b1d6b121a2093ad47f5fbd110b0540 100644 --- a/src/server/memsto/alert_subsribe_cache.go +++ b/memsto/alert_subscribe_cache.go @@ -5,25 +5,33 @@ import ( "sync" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) type AlertSubscribeCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex subs map[int64][]*models.AlertSubscribe } -var AlertSubscribeCache = AlertSubscribeCacheType{ - statTotal: -1, - statLastUpdated: -1, - subs: make(map[int64][]*models.AlertSubscribe), +func NewAlertSubscribeCache(ctx *ctx.Context, stats *Stats) *AlertSubscribeCacheType { + asc := &AlertSubscribeCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + subs: make(map[int64][]*models.AlertSubscribe), + } + asc.SyncAlertSubscribes() + return asc } func (c *AlertSubscribeCacheType) Reset() { @@ -78,41 +86,41 @@ func (c *AlertSubscribeCacheType) GetStructs(ruleId int64) []models.AlertSubscri return ret } -func SyncAlertSubscribes() { - err := syncAlertSubscribes() +func (c *AlertSubscribeCacheType) SyncAlertSubscribes() { + err := c.syncAlertSubscribes() if err != nil { fmt.Println("failed to sync alert subscribes:", err) exit(1) } - go loopSyncAlertSubscribes() + go c.loopSyncAlertSubscribes() } -func loopSyncAlertSubscribes() { +func (c *AlertSubscribeCacheType) loopSyncAlertSubscribes() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncAlertSubscribes(); err != nil { + if err := c.syncAlertSubscribes(); err != nil { logger.Warning("failed to sync alert subscribes:", err) } } } -func syncAlertSubscribes() error { +func (c *AlertSubscribeCacheType) syncAlertSubscribes() error { start := time.Now() - stat, err := models.AlertSubscribeStatistics("") + stat, err := models.AlertSubscribeStatistics(c.ctx) if err != nil { return errors.WithMessage(err, "failed to exec AlertSubscribeStatistics") } - if !AlertSubscribeCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_alert_subscribes").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_alert_subscribes").Set(0) + if !c.StatChanged(stat.Total, stat.LastUpdated) { + c.stats.GaugeCronDuration.WithLabelValues("sync_alert_subscribes").Set(0) + c.stats.GaugeSyncNumber.WithLabelValues("sync_alert_subscribes").Set(0) logger.Debug("alert subscribes not changed") return nil } - lst, err := models.AlertSubscribeGetsByCluster("") + lst, err := models.AlertSubscribeGetsAll(c.ctx) if err != nil { return errors.WithMessage(err, "failed to exec AlertSubscribeGetsByCluster") } @@ -126,14 +134,26 @@ func syncAlertSubscribes() error { continue } + err = lst[i].DB2FE() + if err != nil { + logger.Warningf("failed to db2fe alert subscribe, id: %d", lst[i].Id) + continue + } + + err = lst[i].FillDatasourceIds(c.ctx) + if err != nil { + logger.Warningf("failed to fill datasource ids, id: %d", lst[i].Id) + continue + } + subs[lst[i].RuleId] = append(subs[lst[i].RuleId], lst[i]) } - AlertSubscribeCache.Set(subs, stat.Total, stat.LastUpdated) + c.Set(subs, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_alert_subscribes").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_alert_subscribes").Set(float64(len(lst))) + c.stats.GaugeCronDuration.WithLabelValues("sync_alert_subscribes").Set(float64(ms)) + c.stats.GaugeSyncNumber.WithLabelValues("sync_alert_subscribes").Set(float64(len(lst))) logger.Infof("timer: sync subscribes done, cost: %dms, number: %d", ms, len(lst)) return nil diff --git a/memsto/busi_group_cache.go b/memsto/busi_group_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..378ec93561fcb575f556c77d7e36ba9e222ad770 --- /dev/null +++ b/memsto/busi_group_cache.go @@ -0,0 +1,111 @@ +package memsto + +import ( + "log" + "sync" + "time" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + + "github.com/pkg/errors" + "github.com/toolkits/pkg/logger" +) + +type BusiGroupCacheType struct { + statTotal int64 + statLastUpdated int64 + ctx *ctx.Context + stats *Stats + + sync.RWMutex + ugs map[int64]*models.BusiGroup // key: id +} + +func NewBusiGroupCache(ctx *ctx.Context, stats *Stats) *BusiGroupCacheType { + bg := &BusiGroupCacheType{ + statTotal: -1, + statLastUpdated: -1, + ugs: make(map[int64]*models.BusiGroup), + ctx: ctx, + stats: stats, + } + + bg.SyncBusiGroups() + return bg +} + +func (c *BusiGroupCacheType) StatChanged(total, lastUpdated int64) bool { + if c.statTotal == total && c.statLastUpdated == lastUpdated { + return false + } + + return true +} + +func (c *BusiGroupCacheType) Set(ugs map[int64]*models.BusiGroup, total, lastUpdated int64) { + c.Lock() + c.ugs = ugs + c.Unlock() + + // only one goroutine used, so no need lock + c.statTotal = total + c.statLastUpdated = lastUpdated +} + +func (c *BusiGroupCacheType) GetByBusiGroupId(id int64) *models.BusiGroup { + c.RLock() + defer c.RUnlock() + return c.ugs[id] +} + +func (c *BusiGroupCacheType) SyncBusiGroups() { + err := c.syncBusiGroups() + if err != nil { + log.Fatalln("failed to sync busi groups:", err) + } + + go c.loopSyncBusiGroups() +} + +func (c *BusiGroupCacheType) loopSyncBusiGroups() { + duration := time.Duration(9000) * time.Millisecond + for { + time.Sleep(duration) + if err := c.syncBusiGroups(); err != nil { + logger.Warning("failed to sync busi groups:", err) + } + } +} + +func (c *BusiGroupCacheType) syncBusiGroups() error { + start := time.Now() + + stat, err := models.BusiGroupStatistics(c.ctx) + if err != nil { + return errors.WithMessage(err, "failed to call BusiGroupStatistics") + } + + if !c.StatChanged(stat.Total, stat.LastUpdated) { + c.stats.GaugeCronDuration.WithLabelValues("sync_busi_groups").Set(0) + c.stats.GaugeSyncNumber.WithLabelValues("sync_busi_groups").Set(0) + + logger.Debug("busi_group not changed") + return nil + } + + m, err := models.BusiGroupGetMap(c.ctx) + if err != nil { + return errors.WithMessage(err, "failed to call BusiGroupGetMap") + } + + c.Set(m, stat.Total, stat.LastUpdated) + + ms := time.Since(start).Milliseconds() + c.stats.GaugeCronDuration.WithLabelValues("sync_busi_groups").Set(float64(ms)) + c.stats.GaugeSyncNumber.WithLabelValues("sync_busi_groups").Set(float64(len(m))) + + logger.Infof("timer: sync busi groups done, cost: %dms, number: %d", ms, len(m)) + + return nil +} diff --git a/memsto/datasource_cache.go b/memsto/datasource_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..a395f7fc909765619d0a2487ceb3e1756b1f9db3 --- /dev/null +++ b/memsto/datasource_cache.go @@ -0,0 +1,110 @@ +package memsto + +import ( + "log" + "sync" + "time" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + + "github.com/pkg/errors" + "github.com/toolkits/pkg/logger" +) + +type DatasourceCacheType struct { + statTotal int64 + statLastUpdated int64 + ctx *ctx.Context + stats *Stats + + sync.RWMutex + ds map[int64]*models.Datasource // key: id +} + +func NewDatasourceCache(ctx *ctx.Context, stats *Stats) *DatasourceCacheType { + ds := &DatasourceCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + ds: make(map[int64]*models.Datasource), + } + ds.SyncDatasources() + return ds +} + +func (d *DatasourceCacheType) StatChanged(total, lastUpdated int64) bool { + if d.statTotal == total && d.statLastUpdated == lastUpdated { + return false + } + + return true +} + +func (d *DatasourceCacheType) Set(ds map[int64]*models.Datasource, total, lastUpdated int64) { + d.Lock() + d.ds = ds + d.Unlock() + + // only one goroutine used, so no need lock + d.statTotal = total + d.statLastUpdated = lastUpdated +} + +func (d *DatasourceCacheType) GetBId(id int64) *models.Datasource { + d.RLock() + defer d.RUnlock() + return d.ds[id] +} + +func (d *DatasourceCacheType) SyncDatasources() { + err := d.syncDatasources() + if err != nil { + log.Fatalln("failed to sync datasources:", err) + } + + go d.loopSyncDatasources() +} + +func (d *DatasourceCacheType) loopSyncDatasources() { + duration := time.Duration(9000) * time.Millisecond + for { + time.Sleep(duration) + if err := d.syncDatasources(); err != nil { + logger.Warning("failed to sync datasources:", err) + } + } +} + +func (d *DatasourceCacheType) syncDatasources() error { + start := time.Now() + + stat, err := models.DatasourceStatistics(d.ctx) + if err != nil { + return errors.WithMessage(err, "failed to call DatasourceStatistics") + } + + if !d.StatChanged(stat.Total, stat.LastUpdated) { + d.stats.GaugeCronDuration.WithLabelValues("sync_datasources").Set(0) + d.stats.GaugeSyncNumber.WithLabelValues("sync_datasources").Set(0) + + logger.Debug("datasource not changed") + return nil + } + + m, err := models.DatasourceGetMap(d.ctx) + if err != nil { + return errors.WithMessage(err, "failed to call DatasourceGetMap") + } + + d.Set(m, stat.Total, stat.LastUpdated) + + ms := time.Since(start).Milliseconds() + d.stats.GaugeCronDuration.WithLabelValues("sync_datasources").Set(float64(ms)) + d.stats.GaugeSyncNumber.WithLabelValues("sync_datasources").Set(float64(len(m))) + + logger.Infof("timer: sync datasources done, cost: %dms, number: %d", ms, len(m)) + + return nil +} diff --git a/memsto/memsto.go b/memsto/memsto.go new file mode 100644 index 0000000000000000000000000000000000000000..3b4eeaca01d695362815abc3938620f9b4b21d02 --- /dev/null +++ b/memsto/memsto.go @@ -0,0 +1,13 @@ +package memsto + +import ( + "os" + + "github.com/toolkits/pkg/logger" +) + +// TODO 优化 exit 处理方式 +func exit(code int) { + logger.Close() + os.Exit(code) +} diff --git a/memsto/notify_script.go b/memsto/notify_script.go new file mode 100644 index 0000000000000000000000000000000000000000..de7ff91ce9a0fdd663dc939488dc3b479860a82f --- /dev/null +++ b/memsto/notify_script.go @@ -0,0 +1,62 @@ +package memsto + +import ( + "encoding/json" + "sync" + "time" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/toolkits/pkg/logger" +) + +type NotifyScriptCacheType struct { + ctx *ctx.Context + config models.NotifyScript + sync.RWMutex +} + +func NewNotifyScript(ctx *ctx.Context) *NotifyScriptCacheType { + w := &NotifyScriptCacheType{ + ctx: ctx, + } + w.SyncNotifyScript() + return w +} + +func (w *NotifyScriptCacheType) SyncNotifyScript() { + err := w.syncNotifyScript() + if err != nil { + logger.Errorf("failed to sync notify config:", err) + } + + go w.loopSyncNotifyScript() +} + +func (w *NotifyScriptCacheType) loopSyncNotifyScript() { + duration := time.Duration(9000) * time.Millisecond + for { + time.Sleep(duration) + if err := w.syncNotifyScript(); err != nil { + logger.Warning("failed to sync notify config:", err) + } + } +} + +func (w *NotifyScriptCacheType) syncNotifyScript() error { + cval, err := models.ConfigsGet(w.ctx, models.NOTIFYSCRIPT) + if err != nil { + return err + } + w.RWMutex.Lock() + json.Unmarshal([]byte(cval), &w.config) + w.RWMutex.Unlock() + logger.Infof("timer: sync notify done") + return nil +} + +func (w *NotifyScriptCacheType) GetNotifyScript() models.NotifyScript { + w.RWMutex.RLock() + defer w.RWMutex.RUnlock() + return w.config +} diff --git a/src/server/memsto/recording_rule_cache.go b/memsto/recording_rule_cache.go similarity index 61% rename from src/server/memsto/recording_rule_cache.go rename to memsto/recording_rule_cache.go index 9afbbac893f7d8e08d09a7a309da8656cf422971..6726405536fce8b8e05023494df6a4d48abdb0f2 100644 --- a/src/server/memsto/recording_rule_cache.go +++ b/memsto/recording_rule_cache.go @@ -5,8 +5,9 @@ import ( "sync" "time" - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" ) @@ -14,15 +15,23 @@ import ( type RecordingRuleCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex rules map[int64]*models.RecordingRule // key: rule id } -var RecordingRuleCache = RecordingRuleCacheType{ - statTotal: -1, - statLastUpdated: -1, - rules: make(map[int64]*models.RecordingRule), +func NewRecordingRuleCache(ctx *ctx.Context, stats *Stats) *RecordingRuleCacheType { + rrc := &RecordingRuleCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + rules: make(map[int64]*models.RecordingRule), + } + rrc.SyncRecordingRules() + return rrc } func (rrc *RecordingRuleCacheType) Reset() { @@ -71,42 +80,42 @@ func (rrc *RecordingRuleCacheType) GetRuleIds() []int64 { return list } -func SyncRecordingRules() { - err := syncRecordingRules() +func (rrc *RecordingRuleCacheType) SyncRecordingRules() { + err := rrc.syncRecordingRules() if err != nil { fmt.Println("failed to sync recording rules:", err) exit(1) } - go loopSyncRecordingRules() + go rrc.loopSyncRecordingRules() } -func loopSyncRecordingRules() { +func (rrc *RecordingRuleCacheType) loopSyncRecordingRules() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncRecordingRules(); err != nil { + if err := rrc.syncRecordingRules(); err != nil { logger.Warning("failed to sync recording rules:", err) } } } -func syncRecordingRules() error { +func (rrc *RecordingRuleCacheType) syncRecordingRules() error { start := time.Now() - stat, err := models.RecordingRuleStatistics("") + stat, err := models.RecordingRuleStatistics(rrc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec RecordingRuleStatistics") } - if !RecordingRuleCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_recording_rules").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_recording_rules").Set(0) + if !rrc.StatChanged(stat.Total, stat.LastUpdated) { + rrc.stats.GaugeCronDuration.WithLabelValues("sync_recording_rules").Set(0) + rrc.stats.GaugeSyncNumber.WithLabelValues("sync_recording_rules").Set(0) logger.Debug("recoding rules not changed") return nil } - lst, err := models.RecordingRuleGetsByCluster("") + lst, err := models.RecordingRuleGetsByCluster(rrc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec RecordingRuleGetsByCluster") } @@ -116,11 +125,11 @@ func syncRecordingRules() error { m[lst[i].Id] = lst[i] } - RecordingRuleCache.Set(m, stat.Total, stat.LastUpdated) + rrc.Set(m, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_recording_rules").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_recording_rules").Set(float64(len(m))) + rrc.stats.GaugeCronDuration.WithLabelValues("sync_recording_rules").Set(float64(ms)) + rrc.stats.GaugeSyncNumber.WithLabelValues("sync_recording_rules").Set(float64(len(m))) logger.Infof("timer: sync recording rules done, cost: %dms, number: %d", ms, len(m)) return nil diff --git a/memsto/stat.go b/memsto/stat.go new file mode 100644 index 0000000000000000000000000000000000000000..a49d4a3d6766bfc83b7ea73bc223f3bef0cfd867 --- /dev/null +++ b/memsto/stat.go @@ -0,0 +1,34 @@ +package memsto + +import "github.com/prometheus/client_golang/prometheus" + +type Stats struct { + GaugeCronDuration *prometheus.GaugeVec + GaugeSyncNumber *prometheus.GaugeVec +} + +func NewSyncStats() *Stats { + GaugeCronDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "n9e", + Subsystem: "cron", + Name: "duration", + Help: "Cron method use duration, unit: ms.", + }, []string{"name"}) + + GaugeSyncNumber := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "n9e", + Subsystem: "cron", + Name: "sync_number", + Help: "Cron sync number.", + }, []string{"name"}) + + prometheus.MustRegister( + GaugeCronDuration, + GaugeSyncNumber, + ) + + return &Stats{ + GaugeCronDuration: GaugeCronDuration, + GaugeSyncNumber: GaugeSyncNumber, + } +} diff --git a/src/server/memsto/target_cache.go b/memsto/target_cache.go similarity index 62% rename from src/server/memsto/target_cache.go rename to memsto/target_cache.go index 78d4b589e0283992a275489a0a7ac272c3ffb631..540dae500718d9cdf59d12334e539d3c4b063c41 100644 --- a/src/server/memsto/target_cache.go +++ b/memsto/target_cache.go @@ -1,17 +1,16 @@ package memsto import ( - "fmt" + "log" "strings" "sync" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) // 1. append note to alert_event @@ -19,16 +18,24 @@ import ( type TargetCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex targets map[string]*models.Target // key: ident } -// init TargetCache -var TargetCache = TargetCacheType{ - statTotal: -1, - statLastUpdated: -1, - targets: make(map[string]*models.Target), +func NewTargetCache(ctx *ctx.Context, stats *Stats) *TargetCacheType { + tc := &TargetCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + targets: make(map[string]*models.Target), + } + + tc.SyncTargets() + return tc } func (tc *TargetCacheType) Reset() { @@ -80,51 +87,43 @@ func (tc *TargetCacheType) GetDeads(actives map[string]struct{}) map[string]*mod return ret } -func SyncTargets() { - err := syncTargets() +func (tc *TargetCacheType) SyncTargets() { + err := tc.syncTargets() if err != nil { - fmt.Println("failed to sync targets:", err) - exit(1) + log.Fatalln("failed to sync targets:", err) } - go loopSyncTargets() + go tc.loopSyncTargets() } -func loopSyncTargets() { +func (tc *TargetCacheType) loopSyncTargets() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncTargets(); err != nil { + if err := tc.syncTargets(); err != nil { logger.Warning("failed to sync targets:", err) } } } -func syncTargets() error { +func (tc *TargetCacheType) syncTargets() error { start := time.Now() - clusterName := config.C.ClusterName - if clusterName == "" { - TargetCache.Reset() - logger.Warning("cluster name is blank") - return nil - } - - stat, err := models.TargetStatistics(clusterName) + stat, err := models.TargetStatistics(tc.ctx) if err != nil { - return errors.WithMessage(err, "failed to exec TargetStatistics") + return errors.WithMessage(err, "failed to call TargetStatistics") } - if !TargetCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_targets").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_targets").Set(0) + if !tc.StatChanged(stat.Total, stat.LastUpdated) { + tc.stats.GaugeCronDuration.WithLabelValues("sync_targets").Set(0) + tc.stats.GaugeSyncNumber.WithLabelValues("sync_targets").Set(0) logger.Debug("targets not changed") return nil } - lst, err := models.TargetGetsByCluster(clusterName) + lst, err := models.TargetGetsAll(tc.ctx) if err != nil { - return errors.WithMessage(err, "failed to exec TargetGetsByCluster") + return errors.WithMessage(err, "failed to call TargetGetsAll") } m := make(map[string]*models.Target) @@ -142,11 +141,11 @@ func syncTargets() error { m[lst[i].Ident] = lst[i] } - TargetCache.Set(m, stat.Total, stat.LastUpdated) + tc.Set(m, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_targets").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_targets").Set(float64(len(lst))) + tc.stats.GaugeCronDuration.WithLabelValues("sync_targets").Set(float64(ms)) + tc.stats.GaugeSyncNumber.WithLabelValues("sync_targets").Set(float64(len(lst))) logger.Infof("timer: sync targets done, cost: %dms, number: %d", ms, len(lst)) return nil diff --git a/src/server/memsto/user_cache.go b/memsto/user_cache.go similarity index 67% rename from src/server/memsto/user_cache.go rename to memsto/user_cache.go index d016d4a62fd372ced816dd8786c387b2e8912b8b..cb9aac8e1bed63979a12f4fe0b9185f9b75bd2b9 100644 --- a/src/server/memsto/user_cache.go +++ b/memsto/user_cache.go @@ -5,25 +5,33 @@ import ( "sync" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) type UserCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex users map[int64]*models.User // key: id } -var UserCache = UserCacheType{ - statTotal: -1, - statLastUpdated: -1, - users: make(map[int64]*models.User), +func NewUserCache(ctx *ctx.Context, stats *Stats) *UserCacheType { + uc := &UserCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + users: make(map[int64]*models.User), + } + uc.SyncUsers() + return uc } func (uc *UserCacheType) StatChanged(total, lastUpdated int64) bool { @@ -95,43 +103,43 @@ func (uc *UserCacheType) GetMaintainerUsers() []*models.User { return users } -func SyncUsers() { - err := syncUsers() +func (uc *UserCacheType) SyncUsers() { + err := uc.syncUsers() if err != nil { fmt.Println("failed to sync users:", err) exit(1) } - go loopSyncUsers() + go uc.loopSyncUsers() } -func loopSyncUsers() { +func (uc *UserCacheType) loopSyncUsers() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncUsers(); err != nil { + if err := uc.syncUsers(); err != nil { logger.Warning("failed to sync users:", err) } } } -func syncUsers() error { +func (uc *UserCacheType) syncUsers() error { start := time.Now() - stat, err := models.UserStatistics() + stat, err := models.UserStatistics(uc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec UserStatistics") } - if !UserCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_users").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_users").Set(0) + if !uc.StatChanged(stat.Total, stat.LastUpdated) { + uc.stats.GaugeCronDuration.WithLabelValues("sync_users").Set(0) + uc.stats.GaugeSyncNumber.WithLabelValues("sync_users").Set(0) logger.Debug("users not changed") return nil } - lst, err := models.UserGetAll() + lst, err := models.UserGetAll(uc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec UserGetAll") } @@ -141,11 +149,11 @@ func syncUsers() error { m[lst[i].Id] = lst[i] } - UserCache.Set(m, stat.Total, stat.LastUpdated) + uc.Set(m, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_users").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_users").Set(float64(len(m))) + uc.stats.GaugeCronDuration.WithLabelValues("sync_users").Set(float64(ms)) + uc.stats.GaugeSyncNumber.WithLabelValues("sync_users").Set(float64(len(m))) logger.Infof("timer: sync users done, cost: %dms, number: %d", ms, len(m)) diff --git a/src/server/memsto/user_group_cache.go b/memsto/user_group_cache.go similarity index 64% rename from src/server/memsto/user_group_cache.go rename to memsto/user_group_cache.go index 4fdb13f0ce64c4602616d24c24a8a750b33408d7..b7aa877e934a5de47775b941d0f825cb4d8bb466 100644 --- a/src/server/memsto/user_group_cache.go +++ b/memsto/user_group_cache.go @@ -5,25 +5,33 @@ import ( "sync" "time" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" ) type UserGroupCacheType struct { statTotal int64 statLastUpdated int64 + ctx *ctx.Context + stats *Stats sync.RWMutex ugs map[int64]*models.UserGroup // key: id } -var UserGroupCache = UserGroupCacheType{ - statTotal: -1, - statLastUpdated: -1, - ugs: make(map[int64]*models.UserGroup), +func NewUserGroupCache(ctx *ctx.Context, stats *Stats) *UserGroupCacheType { + ugc := &UserGroupCacheType{ + statTotal: -1, + statLastUpdated: -1, + ctx: ctx, + stats: stats, + ugs: make(map[int64]*models.UserGroup), + } + ugc.SyncUserGroups() + return ugc } func (ugc *UserGroupCacheType) StatChanged(total, lastUpdated int64) bool { @@ -77,43 +85,43 @@ func (ugc *UserGroupCacheType) GetByUserGroupIds(ids []int64) []*models.UserGrou return ugs } -func SyncUserGroups() { - err := syncUserGroups() +func (ugc *UserGroupCacheType) SyncUserGroups() { + err := ugc.syncUserGroups() if err != nil { fmt.Println("failed to sync user groups:", err) exit(1) } - go loopSyncUserGroups() + go ugc.loopSyncUserGroups() } -func loopSyncUserGroups() { +func (ugc *UserGroupCacheType) loopSyncUserGroups() { duration := time.Duration(9000) * time.Millisecond for { time.Sleep(duration) - if err := syncUserGroups(); err != nil { + if err := ugc.syncUserGroups(); err != nil { logger.Warning("failed to sync user groups:", err) } } } -func syncUserGroups() error { +func (ugc *UserGroupCacheType) syncUserGroups() error { start := time.Now() - stat, err := models.UserGroupStatistics() + stat, err := models.UserGroupStatistics(ugc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec UserGroupStatistics") } - if !UserGroupCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_user_groups").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_user_groups").Set(0) + if !ugc.StatChanged(stat.Total, stat.LastUpdated) { + ugc.stats.GaugeCronDuration.WithLabelValues("sync_user_groups").Set(0) + ugc.stats.GaugeSyncNumber.WithLabelValues("sync_user_groups").Set(0) logger.Debug("user_group not changed") return nil } - lst, err := models.UserGroupGetAll() + lst, err := models.UserGroupGetAll(ugc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec UserGroupGetAll") } @@ -124,7 +132,7 @@ func syncUserGroups() error { } // fill user ids - members, err := models.UserGroupMemberGetAll() + members, err := models.UserGroupMemberGetAll(ugc.ctx) if err != nil { return errors.WithMessage(err, "failed to exec UserGroupMemberGetAll") } @@ -142,11 +150,11 @@ func syncUserGroups() error { ug.UserIds = append(ug.UserIds, members[i].UserId) } - UserGroupCache.Set(m, stat.Total, stat.LastUpdated) + ugc.Set(m, stat.Total, stat.LastUpdated) ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_user_groups").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_user_groups").Set(float64(len(m))) + ugc.stats.GaugeCronDuration.WithLabelValues("sync_user_groups").Set(float64(ms)) + ugc.stats.GaugeSyncNumber.WithLabelValues("sync_user_groups").Set(float64(len(m))) logger.Infof("timer: sync user groups done, cost: %dms, number: %d", ms, len(m)) diff --git a/memsto/webhook.go b/memsto/webhook.go new file mode 100644 index 0000000000000000000000000000000000000000..52a897b48168bb7a03e37f6a6e05ae3780fdd2cf --- /dev/null +++ b/memsto/webhook.go @@ -0,0 +1,63 @@ +package memsto + +import ( + "encoding/json" + "sync" + "time" + + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/toolkits/pkg/logger" +) + +type WebhookCacheType struct { + ctx *ctx.Context + webhooks []*models.Webhook + + sync.RWMutex +} + +func NewWebhookCache(ctx *ctx.Context) *WebhookCacheType { + w := &WebhookCacheType{ + ctx: ctx, + } + w.SyncWebhooks() + return w +} + +func (w *WebhookCacheType) SyncWebhooks() { + err := w.syncWebhooks() + if err != nil { + logger.Errorf("failed to sync webhooks:", err) + } + + go w.loopSyncWebhooks() +} + +func (w *WebhookCacheType) loopSyncWebhooks() { + duration := time.Duration(9000) * time.Millisecond + for { + time.Sleep(duration) + if err := w.syncWebhooks(); err != nil { + logger.Warning("failed to sync webhooks:", err) + } + } +} + +func (w *WebhookCacheType) syncWebhooks() error { + cval, err := models.ConfigsGet(w.ctx, models.WEBHOOKKEY) + if err != nil { + return err + } + w.RWMutex.Lock() + json.Unmarshal([]byte(cval), &w.webhooks) + w.RWMutex.Unlock() + logger.Infof("timer: sync wbhooks done number: %d", len(w.webhooks)) + return nil +} + +func (w *WebhookCacheType) GetWebhooks() []*models.Webhook { + w.RWMutex.RLock() + defer w.RWMutex.RUnlock() + return w.webhooks +} diff --git a/src/models/alert_aggr_view.go b/models/alert_aggr_view.go similarity index 71% rename from src/models/alert_aggr_view.go rename to models/alert_aggr_view.go index 87875b0a73233dfb2f42c4502a1b7152bb382386..ab655a56fe34d474cbaf02f7f8cafe5a056541e0 100644 --- a/src/models/alert_aggr_view.go +++ b/models/alert_aggr_view.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" "github.com/toolkits/pkg/slice" ) @@ -70,7 +71,7 @@ func (v *AlertAggrView) Verify() error { return nil } -func (v *AlertAggrView) Add() error { +func (v *AlertAggrView) Add(ctx *ctx.Context) error { if err := v.Verify(); err != nil { return err } @@ -79,10 +80,10 @@ func (v *AlertAggrView) Add() error { v.CreateAt = now v.UpdateAt = now v.Cate = 1 - return Insert(v) + return Insert(ctx, v) } -func (v *AlertAggrView) Update(name, rule string, cate int, createBy int64) error { +func (v *AlertAggrView) Update(ctx *ctx.Context, name, rule string, cate int, createBy int64) error { if err := v.Verify(); err != nil { return err } @@ -96,25 +97,25 @@ func (v *AlertAggrView) Update(name, rule string, cate int, createBy int64) erro v.CreateBy = createBy } - return DB().Model(v).Select("name", "rule", "cate", "update_at", "create_by").Updates(v).Error + return DB(ctx).Model(v).Select("name", "rule", "cate", "update_at", "create_by").Updates(v).Error } // AlertAggrViewDel: userid for safe delete -func AlertAggrViewDel(ids []int64, createBy ...interface{}) error { +func AlertAggrViewDel(ctx *ctx.Context, ids []int64, createBy ...interface{}) error { if len(ids) == 0 { return nil } if len(createBy) > 0 { - return DB().Where("id in ? and create_by = ?", ids, createBy).Delete(new(AlertAggrView)).Error + return DB(ctx).Where("id in ? and create_by = ?", ids, createBy).Delete(new(AlertAggrView)).Error } - return DB().Where("id in ?", ids).Delete(new(AlertAggrView)).Error + return DB(ctx).Where("id in ?", ids).Delete(new(AlertAggrView)).Error } -func AlertAggrViewGets(createBy interface{}) ([]AlertAggrView, error) { +func AlertAggrViewGets(ctx *ctx.Context, createBy interface{}) ([]AlertAggrView, error) { var lst []AlertAggrView - err := DB().Where("create_by = ? or cate = 0", createBy).Find(&lst).Error + err := DB(ctx).Where("create_by = ? or cate = 0", createBy).Find(&lst).Error if err == nil && len(lst) > 1 { sort.Slice(lst, func(i, j int) bool { if lst[i].Cate < lst[j].Cate { @@ -131,9 +132,9 @@ func AlertAggrViewGets(createBy interface{}) ([]AlertAggrView, error) { return lst, err } -func AlertAggrViewGet(where string, args ...interface{}) (*AlertAggrView, error) { +func AlertAggrViewGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertAggrView, error) { var lst []*AlertAggrView - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } diff --git a/src/models/alert_cur_event.go b/models/alert_cur_event.go similarity index 67% rename from src/models/alert_cur_event.go rename to models/alert_cur_event.go index 5e7528610fb82cee133979710f951f2f9dacc86d..8a01ef2a2eb05b39c40528d84ea866602c3e1389 100644 --- a/src/models/alert_cur_event.go +++ b/models/alert_cur_event.go @@ -2,18 +2,22 @@ package models import ( "bytes" + "encoding/json" "fmt" "strconv" "strings" "text/template" - "github.com/didi/nightingale/v5/src/pkg/tplx" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/tplx" + "github.com/toolkits/pkg/logger" ) type AlertCurEvent struct { Id int64 `json:"id" gorm:"primaryKey"` Cate string `json:"cate"` Cluster string `json:"cluster"` + DatasourceId int64 `json:"datasource_id"` GroupId int64 `json:"group_id"` // busi group id GroupName string `json:"group_name"` // busi group name Hash string `json:"hash"` // rule_id + vector_key @@ -25,6 +29,8 @@ type AlertCurEvent struct { Severity int `json:"severity"` PromForDuration int `json:"prom_for_duration"` PromQl string `json:"prom_ql"` + RuleConfig string `json:"-" gorm:"rule_config"` // rule config + RuleConfigJson interface{} `json:"rule_config" gorm:"-"` // rule config for fe PromEvalInterval int `json:"prom_eval_interval"` Callbacks string `json:"-"` // for db CallbacksJSON []string `json:"callbacks" gorm:"-"` // for fe @@ -42,6 +48,8 @@ type AlertCurEvent struct { Tags string `json:"-"` // for db TagsJSON []string `json:"tags" gorm:"-"` // for fe TagsMap map[string]string `json:"-" gorm:"-"` // for internal usage + Annotations string `json:"-"` // + AnnotationsJSON map[string]string `json:"annotations" gorm:"-"` // for fe IsRecovered bool `json:"is_recovered" gorm:"-"` // for notify.py NotifyUsersObj []*User `json:"notify_users_obj" gorm:"-"` // for notify.py LastEvalTime int64 `json:"last_eval_time" gorm:"-"` // for notify.py 上次计算的时间 @@ -54,8 +62,8 @@ func (e *AlertCurEvent) TableName() string { return "alert_cur_event" } -func (e *AlertCurEvent) Add() error { - return Insert(e) +func (e *AlertCurEvent) Add(ctx *ctx.Context) error { + return Insert(ctx, e) } type AggrRule struct { @@ -95,6 +103,12 @@ func (e *AlertCurEvent) ParseRule(field string) error { if field == "rule_note" { e.RuleNote = body.String() } + + if field == "annotations" { + e.Annotations = body.String() + json.Unmarshal([]byte(e.Annotations), &e.AnnotationsJSON) + } + return nil } @@ -150,12 +164,16 @@ func (e *AlertCurEvent) GetField(field string) string { return e.TargetIdent case "target_note": return e.TargetNote + case "callbacks": + return e.Callbacks + case "annotations": + return e.Annotations default: return "" } } -func (e *AlertCurEvent) ToHis() *AlertHisEvent { +func (e *AlertCurEvent) ToHis(ctx *ctx.Context) *AlertHisEvent { isRecovered := 0 var recoverTime int64 = 0 if e.IsRecovered { @@ -167,6 +185,7 @@ func (e *AlertCurEvent) ToHis() *AlertHisEvent { IsRecovered: isRecovered, Cate: e.Cate, Cluster: e.Cluster, + DatasourceId: e.DatasourceId, GroupId: e.GroupId, GroupName: e.GroupName, Hash: e.Hash, @@ -179,11 +198,15 @@ func (e *AlertCurEvent) ToHis() *AlertHisEvent { PromForDuration: e.PromForDuration, PromQl: e.PromQl, PromEvalInterval: e.PromEvalInterval, + RuleConfig: e.RuleConfig, + RuleConfigJson: e.RuleConfigJson, Callbacks: e.Callbacks, RunbookUrl: e.RunbookUrl, NotifyRecovered: e.NotifyRecovered, NotifyChannels: e.NotifyChannels, NotifyGroups: e.NotifyGroups, + Annotations: e.Annotations, + AnnotationsJSON: e.AnnotationsJSON, TargetIdent: e.TargetIdent, TargetNote: e.TargetNote, TriggerTime: e.TriggerTime, @@ -196,11 +219,13 @@ func (e *AlertCurEvent) ToHis() *AlertHisEvent { } } -func (e *AlertCurEvent) DB2FE() { +func (e *AlertCurEvent) DB2FE(ctx *ctx.Context) { e.NotifyChannelsJSON = strings.Fields(e.NotifyChannels) e.NotifyGroupsJSON = strings.Fields(e.NotifyGroups) e.CallbacksJSON = strings.Fields(e.Callbacks) e.TagsJSON = strings.Split(e.Tags, ",,") + json.Unmarshal([]byte(e.Annotations), &e.AnnotationsJSON) + json.Unmarshal([]byte(e.RuleConfig), &e.RuleConfigJson) } func (e *AlertCurEvent) DB2Mem() { @@ -226,7 +251,7 @@ func (e *AlertCurEvent) DB2Mem() { } // for webui -func (e *AlertCurEvent) FillNotifyGroups(cache map[int64]*UserGroup) error { +func (e *AlertCurEvent) FillNotifyGroups(ctx *ctx.Context, cache map[int64]*UserGroup) error { // some user-group already deleted ? count := len(e.NotifyGroupsJSON) if count == 0 { @@ -246,7 +271,7 @@ func (e *AlertCurEvent) FillNotifyGroups(cache map[int64]*UserGroup) error { continue } - ug, err = UserGroupGetById(id) + ug, err = UserGroupGetById(ctx, id) if err != nil { return err } @@ -260,8 +285,12 @@ func (e *AlertCurEvent) FillNotifyGroups(cache map[int64]*UserGroup) error { return nil } -func AlertCurEventTotal(prod string, bgid, stime, etime int64, severity int, clusters, cates []string, query string) (int64, error) { - session := DB().Model(&AlertCurEvent{}).Where("trigger_time between ? and ? and rule_prod = ?", stime, etime, prod) +func AlertCurEventTotal(ctx *ctx.Context, prods []string, bgid, stime, etime int64, severity int, dsIds []int64, cates []string, query string) (int64, error) { + session := DB(ctx).Model(&AlertCurEvent{}).Where("trigger_time between ? and ?", stime, etime) + + if len(prods) != 0 { + session = session.Where("rule_prod in ?", prods) + } if bgid > 0 { session = session.Where("group_id = ?", bgid) @@ -271,8 +300,8 @@ func AlertCurEventTotal(prod string, bgid, stime, etime int64, severity int, clu session = session.Where("severity = ?", severity) } - if len(clusters) > 0 { - session = session.Where("cluster in ?", clusters) + if len(dsIds) > 0 { + session = session.Where("datasource_id in ?", dsIds) } if len(cates) > 0 { @@ -290,8 +319,12 @@ func AlertCurEventTotal(prod string, bgid, stime, etime int64, severity int, clu return Count(session) } -func AlertCurEventGets(prod string, bgid, stime, etime int64, severity int, clusters, cates []string, query string, limit, offset int) ([]AlertCurEvent, error) { - session := DB().Where("trigger_time between ? and ? and rule_prod = ?", stime, etime, prod) +func AlertCurEventGets(ctx *ctx.Context, prods []string, bgid, stime, etime int64, severity int, dsIds []int64, cates []string, query string, limit, offset int) ([]AlertCurEvent, error) { + session := DB(ctx).Where("trigger_time between ? and ?", stime, etime) + + if len(prods) != 0 { + session = session.Where("rule_prod in ?", prods) + } if bgid > 0 { session = session.Where("group_id = ?", bgid) @@ -301,8 +334,8 @@ func AlertCurEventGets(prod string, bgid, stime, etime int64, severity int, clus session = session.Where("severity = ?", severity) } - if len(clusters) > 0 { - session = session.Where("cluster in ?", clusters) + if len(dsIds) > 0 { + session = session.Where("datasource_id in ?", dsIds) } if len(cates) > 0 { @@ -322,32 +355,32 @@ func AlertCurEventGets(prod string, bgid, stime, etime int64, severity int, clus if err == nil { for i := 0; i < len(lst); i++ { - lst[i].DB2FE() + lst[i].DB2FE(ctx) } } return lst, err } -func AlertCurEventDel(ids []int64) error { +func AlertCurEventDel(ctx *ctx.Context, ids []int64) error { if len(ids) == 0 { return nil } - return DB().Where("id in ?", ids).Delete(&AlertCurEvent{}).Error + return DB(ctx).Where("id in ?", ids).Delete(&AlertCurEvent{}).Error } -func AlertCurEventDelByHash(hash string) error { - return DB().Where("hash = ?", hash).Delete(&AlertCurEvent{}).Error +func AlertCurEventDelByHash(ctx *ctx.Context, hash string) error { + return DB(ctx).Where("hash = ?", hash).Delete(&AlertCurEvent{}).Error } -func AlertCurEventExists(where string, args ...interface{}) (bool, error) { - return Exists(DB().Model(&AlertCurEvent{}).Where(where, args...)) +func AlertCurEventExists(ctx *ctx.Context, where string, args ...interface{}) (bool, error) { + return Exists(DB(ctx).Model(&AlertCurEvent{}).Where(where, args...)) } -func AlertCurEventGet(where string, args ...interface{}) (*AlertCurEvent, error) { +func AlertCurEventGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertCurEvent, error) { var lst []*AlertCurEvent - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } @@ -356,14 +389,14 @@ func AlertCurEventGet(where string, args ...interface{}) (*AlertCurEvent, error) return nil, nil } - lst[0].DB2FE() - lst[0].FillNotifyGroups(make(map[int64]*UserGroup)) + lst[0].DB2FE(ctx) + lst[0].FillNotifyGroups(ctx, make(map[int64]*UserGroup)) return lst[0], nil } -func AlertCurEventGetById(id int64) (*AlertCurEvent, error) { - return AlertCurEventGet("id=?", id) +func AlertCurEventGetById(ctx *ctx.Context, id int64) (*AlertCurEvent, error) { + return AlertCurEventGet(ctx, "id=?", id) } type AlertNumber struct { @@ -372,14 +405,14 @@ type AlertNumber struct { } // for busi_group list page -func AlertNumbers(bgids []int64) (map[int64]int64, error) { +func AlertNumbers(ctx *ctx.Context, bgids []int64) (map[int64]int64, error) { ret := make(map[int64]int64) if len(bgids) == 0 { return ret, nil } var arr []AlertNumber - err := DB().Model(&AlertCurEvent{}).Select("group_id", "count(*) as group_count").Where("group_id in ?", bgids).Group("group_id").Find(&arr).Error + err := DB(ctx).Model(&AlertCurEvent{}).Select("group_id", "count(*) as group_count").Where("group_id in ?", bgids).Group("group_id").Find(&arr).Error if err != nil { return nil, err } @@ -391,8 +424,8 @@ func AlertNumbers(bgids []int64) (map[int64]int64, error) { return ret, nil } -func AlertCurEventGetAll(cluster string) ([]*AlertCurEvent, error) { - session := DB().Model(&AlertCurEvent{}) +func AlertCurEventGetAll(ctx *ctx.Context, cluster string) ([]*AlertCurEvent, error) { + session := DB(ctx).Model(&AlertCurEvent{}) if cluster != "" { session = session.Where("cluster = ?", cluster) @@ -403,33 +436,33 @@ func AlertCurEventGetAll(cluster string) ([]*AlertCurEvent, error) { return lst, err } -func AlertCurEventGetByIds(ids []int64) ([]*AlertCurEvent, error) { +func AlertCurEventGetByIds(ctx *ctx.Context, ids []int64) ([]*AlertCurEvent, error) { var lst []*AlertCurEvent if len(ids) == 0 { return lst, nil } - err := DB().Where("id in ?", ids).Order("id desc").Find(&lst).Error + err := DB(ctx).Where("id in ?", ids).Order("id desc").Find(&lst).Error if err == nil { for i := 0; i < len(lst); i++ { - lst[i].DB2FE() + lst[i].DB2FE(ctx) } } return lst, err } -func AlertCurEventGetByRuleIdAndCluster(ruleId int64, cluster string) ([]*AlertCurEvent, error) { +func AlertCurEventGetByRuleIdAndCluster(ctx *ctx.Context, ruleId int64, datasourceId int64) ([]*AlertCurEvent, error) { var lst []*AlertCurEvent - err := DB().Where("rule_id=? and cluster=?", ruleId, cluster).Find(&lst).Error + err := DB(ctx).Where("rule_id=? and datasource_id = ?", ruleId, datasourceId).Find(&lst).Error return lst, err } -func AlertCurEventGetMap(cluster string) (map[int64]map[string]struct{}, error) { - session := DB().Model(&AlertCurEvent{}) +func AlertCurEventGetMap(ctx *ctx.Context, cluster string) (map[int64]map[string]struct{}, error) { + session := DB(ctx).Model(&AlertCurEvent{}) if cluster != "" { - session = session.Where("cluster = ?", cluster) + session = session.Where("datasource_id = ?", cluster) } var lst []*AlertCurEvent @@ -452,3 +485,29 @@ func AlertCurEventGetMap(cluster string) (map[int64]map[string]struct{}, error) return ret, nil } + +func (m *AlertCurEvent) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(m).Updates(fields).Error +} + +func AlertCurEventUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*AlertCurEvent + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + ds, exists := dsm[lst[i].Cluster] + if !exists { + continue + } + lst[i].DatasourceId = ds.Id + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_id": lst[i].DatasourceId}) + if err != nil { + logger.Errorf("update alert rule:%d datasource ids failed, %v", lst[i].Id, err) + } + } + return nil +} diff --git a/models/alert_his_event.go b/models/alert_his_event.go new file mode 100644 index 0000000000000000000000000000000000000000..cf8e5975a6f92edfb4999ac529cae0098ffab611 --- /dev/null +++ b/models/alert_his_event.go @@ -0,0 +1,236 @@ +package models + +import ( + "encoding/json" + "strconv" + "strings" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/toolkits/pkg/logger" +) + +type AlertHisEvent struct { + Id int64 `json:"id" gorm:"primaryKey"` + Cate string `json:"cate"` + IsRecovered int `json:"is_recovered"` + DatasourceId int64 `json:"datasource_id"` + Cluster string `json:"cluster"` + GroupId int64 `json:"group_id"` + GroupName string `json:"group_name"` // busi group name + Hash string `json:"hash"` + RuleId int64 `json:"rule_id"` + RuleName string `json:"rule_name"` + RuleNote string `json:"rule_note"` + RuleProd string `json:"rule_prod"` + RuleAlgo string `json:"rule_algo"` + Severity int `json:"severity"` + PromForDuration int `json:"prom_for_duration"` + PromQl string `json:"prom_ql"` + RuleConfig string `json:"-" gorm:"rule_config"` // rule config + RuleConfigJson interface{} `json:"rule_config" gorm:"-"` // rule config for fe + PromEvalInterval int `json:"prom_eval_interval"` + Callbacks string `json:"-"` + CallbacksJSON []string `json:"callbacks" gorm:"-"` + RunbookUrl string `json:"runbook_url"` + NotifyRecovered int `json:"notify_recovered"` + NotifyChannels string `json:"-"` + NotifyChannelsJSON []string `json:"notify_channels" gorm:"-"` + NotifyGroups string `json:"-"` + NotifyGroupsJSON []string `json:"notify_groups" gorm:"-"` + NotifyGroupsObj []UserGroup `json:"notify_groups_obj" gorm:"-"` + TargetIdent string `json:"target_ident"` + TargetNote string `json:"target_note"` + TriggerTime int64 `json:"trigger_time"` + TriggerValue string `json:"trigger_value"` + RecoverTime int64 `json:"recover_time"` + LastEvalTime int64 `json:"last_eval_time"` + Tags string `json:"-"` + TagsJSON []string `json:"tags" gorm:"-"` + Annotations string `json:"-"` + AnnotationsJSON map[string]string `json:"annotations" gorm:"-"` // for fe + NotifyCurNumber int `json:"notify_cur_number"` // notify: current number + FirstTriggerTime int64 `json:"first_trigger_time"` // 连续告警的首次告警时间 +} + +func (e *AlertHisEvent) TableName() string { + return "alert_his_event" +} + +func (e *AlertHisEvent) Add(ctx *ctx.Context) error { + return Insert(ctx, e) +} + +func (e *AlertHisEvent) DB2FE(ctx *ctx.Context) { + e.NotifyChannelsJSON = strings.Fields(e.NotifyChannels) + e.NotifyGroupsJSON = strings.Fields(e.NotifyGroups) + e.CallbacksJSON = strings.Fields(e.Callbacks) + e.TagsJSON = strings.Split(e.Tags, ",,") + json.Unmarshal([]byte(e.Annotations), &e.AnnotationsJSON) + json.Unmarshal([]byte(e.RuleConfig), &e.RuleConfigJson) +} + +func (e *AlertHisEvent) FillNotifyGroups(ctx *ctx.Context, cache map[int64]*UserGroup) error { + // some user-group already deleted ? + count := len(e.NotifyGroupsJSON) + if count == 0 { + e.NotifyGroupsObj = []UserGroup{} + return nil + } + + for i := range e.NotifyGroupsJSON { + id, err := strconv.ParseInt(e.NotifyGroupsJSON[i], 10, 64) + if err != nil { + continue + } + + ug, has := cache[id] + if has { + e.NotifyGroupsObj = append(e.NotifyGroupsObj, *ug) + continue + } + + ug, err = UserGroupGetById(ctx, id) + if err != nil { + return err + } + + if ug != nil { + e.NotifyGroupsObj = append(e.NotifyGroupsObj, *ug) + cache[id] = ug + } + } + + return nil +} + +func AlertHisEventTotal(ctx *ctx.Context, prods []string, bgid, stime, etime int64, severity int, recovered int, dsIds []int64, cates []string, query string) (int64, error) { + session := DB(ctx).Model(&AlertHisEvent{}).Where("last_eval_time between ? and ?", stime, etime) + + if len(prods) > 0 { + session = session.Where("rule_prod in ?", prods) + } + + if bgid > 0 { + session = session.Where("group_id = ?", bgid) + } + + if severity >= 0 { + session = session.Where("severity = ?", severity) + } + + if recovered >= 0 { + session = session.Where("is_recovered = ?", recovered) + } + + if len(dsIds) > 0 { + session = session.Where("datasource_id in ?", dsIds) + } + + if len(cates) > 0 { + session = session.Where("cate in ?", cates) + } + + if query != "" { + arr := strings.Fields(query) + for i := 0; i < len(arr); i++ { + qarg := "%" + arr[i] + "%" + session = session.Where("rule_name like ? or tags like ?", qarg, qarg) + } + } + + return Count(session) +} + +func AlertHisEventGets(ctx *ctx.Context, prods []string, bgid, stime, etime int64, severity int, recovered int, dsIds []int64, cates []string, query string, limit, offset int) ([]AlertHisEvent, error) { + session := DB(ctx).Where("last_eval_time between ? and ?", stime, etime) + + if len(prods) != 0 { + session = session.Where("rule_prod in ?", prods) + } + + if bgid > 0 { + session = session.Where("group_id = ?", bgid) + } + + if severity >= 0 { + session = session.Where("severity = ?", severity) + } + + if recovered >= 0 { + session = session.Where("is_recovered = ?", recovered) + } + + if len(dsIds) > 0 { + session = session.Where("datasource_id in ?", dsIds) + } + + if len(cates) > 0 { + session = session.Where("cate in ?", cates) + } + + if query != "" { + arr := strings.Fields(query) + for i := 0; i < len(arr); i++ { + qarg := "%" + arr[i] + "%" + session = session.Where("rule_name like ? or tags like ?", qarg, qarg) + } + } + + var lst []AlertHisEvent + err := session.Order("id desc").Limit(limit).Offset(offset).Find(&lst).Error + + if err == nil { + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + } + + return lst, err +} + +func AlertHisEventGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertHisEvent, error) { + var lst []*AlertHisEvent + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + lst[0].DB2FE(ctx) + lst[0].FillNotifyGroups(ctx, make(map[int64]*UserGroup)) + + return lst[0], nil +} + +func AlertHisEventGetById(ctx *ctx.Context, id int64) (*AlertHisEvent, error) { + return AlertHisEventGet(ctx, "id=?", id) +} + +func (m *AlertHisEvent) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(m).Updates(fields).Error +} + +func AlertHisEventUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*AlertHisEvent + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + ds, exists := dsm[lst[i].Cluster] + if !exists { + continue + } + lst[i].DatasourceId = ds.Id + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_id": lst[i].DatasourceId}) + if err != nil { + logger.Errorf("update alert rule:%d datasource ids failed, %v", lst[i].Id, err) + } + } + return nil +} diff --git a/models/alert_mute.go b/models/alert_mute.go new file mode 100644 index 0000000000000000000000000000000000000000..a4aa169151e5f5c8e51e1e365a4f9784a924cb71 --- /dev/null +++ b/models/alert_mute.go @@ -0,0 +1,290 @@ +package models + +import ( + "encoding/json" + "fmt" + "regexp" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/toolkits/pkg/logger" + + "github.com/pkg/errors" +) + +type TagFilter struct { + Key string `json:"key"` // tag key + Func string `json:"func"` // `==` | `=~` | `in` | `!=` | `!~` | `not in` + Value string `json:"value"` // tag value + Regexp *regexp.Regexp // parse value to regexp if func = '=~' or '!~' + Vset map[string]struct{} // parse value to regexp if func = 'in' or 'not in' +} + +const TimeRange int = 0 +const Periodic int = 1 + +type AlertMute struct { + Id int64 `json:"id" gorm:"primaryKey"` + GroupId int64 `json:"group_id"` + Note string `json:"note"` + Cate string `json:"cate"` + Prod string `json:"prod"` + DatasourceIds string `json:"-" gorm:"datasource_ids"` // datasource ids + DatasourceIdsJson []int64 `json:"datasource_ids" gorm:"-"` // for fe + Cluster string `json:"cluster"` // take effect by clusters, seperated by space + Tags ormx.JSONArr `json:"tags"` + Cause string `json:"cause"` + Btime int64 `json:"btime"` + Etime int64 `json:"etime"` + Disabled int `json:"disabled"` // 0: enabled, 1: disabled + CreateBy string `json:"create_by"` + UpdateBy string `json:"update_by"` + CreateAt int64 `json:"create_at"` + UpdateAt int64 `json:"update_at"` + ITags []TagFilter `json:"-" gorm:"-"` // inner tags + MuteTimeType int `json:"mute_time_type"` // 0: mute by time range, 1: mute by periodic time + PeriodicMutes string `json:"-" gorm:"periodic_mutes"` + PeriodicMutesJson []PeriodicMute `json:"periodic_mutes" gorm:"-"` +} + +type PeriodicMute struct { + EnableStime string `json:"enable_stime"` // split by space: "00:00 10:00 12:00" + EnableEtime string `json:"enable_etime"` // split by space: "00:00 10:00 12:00" + EnableDaysOfWeek string `json:"enable_days_of_week"` // eg: "0 1 2 3 4 5 6" +} + +func (m *AlertMute) TableName() string { + return "alert_mute" +} + +func AlertMuteGetById(ctx *ctx.Context, id int64) (*AlertMute, error) { + return AlertMuteGet(ctx, "id=?", id) +} + +func AlertMuteGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertMute, error) { + var lst []*AlertMute + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + err = lst[0].DB2FE(ctx) + return lst[0], err +} + +func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, query string) (lst []AlertMute, err error) { + session := DB(ctx).Where("group_id = ? and prod in (?)", bgid, prods) + + if query != "" { + arr := strings.Fields(query) + for i := 0; i < len(arr); i++ { + qarg := "%" + arr[i] + "%" + session = session.Where("cause like ?", qarg) + } + } + + err = session.Order("id desc").Find(&lst).Error + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + return +} + +func AlertMuteGetsByBG(ctx *ctx.Context, groupId int64) (lst []AlertMute, err error) { + err = DB(ctx).Where("group_id=?", groupId).Order("id desc").Find(&lst).Error + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + return +} + +func (m *AlertMute) Verify() error { + if m.GroupId < 0 { + return errors.New("group_id invalid") + } + + if IsAllDatasource(m.DatasourceIdsJson) { + m.DatasourceIdsJson = []int64{0} + } + + if m.Etime <= m.Btime { + return fmt.Errorf("oops... etime(%d) <= btime(%d)", m.Etime, m.Btime) + } + + if err := m.Parse(); err != nil { + return err + } + + if len(m.ITags) == 0 { + return errors.New("tags is blank") + } + + return nil +} + +func (m *AlertMute) Parse() error { + err := json.Unmarshal(m.Tags, &m.ITags) + if err != nil { + return err + } + + for i := 0; i < len(m.ITags); i++ { + if m.ITags[i].Func == "=~" || m.ITags[i].Func == "!~" { + m.ITags[i].Regexp, err = regexp.Compile(m.ITags[i].Value) + if err != nil { + return err + } + } else if m.ITags[i].Func == "in" || m.ITags[i].Func == "not in" { + arr := strings.Fields(m.ITags[i].Value) + m.ITags[i].Vset = make(map[string]struct{}) + for j := 0; j < len(arr); j++ { + m.ITags[i].Vset[arr[j]] = struct{}{} + } + } + } + + return nil +} + +func (m *AlertMute) Add(ctx *ctx.Context) error { + if err := m.Verify(); err != nil { + return err + } + + if err := m.FE2DB(); err != nil { + return err + } + + now := time.Now().Unix() + m.CreateAt = now + m.UpdateAt = now + return Insert(ctx, m) +} + +func (m *AlertMute) Update(ctx *ctx.Context, arm AlertMute) error { + + arm.Id = m.Id + arm.GroupId = m.GroupId + arm.CreateAt = m.CreateAt + arm.CreateBy = m.CreateBy + arm.UpdateAt = time.Now().Unix() + + err := arm.Verify() + if err != nil { + return err + } + + if err := arm.FE2DB(); err != nil { + return err + } + + return DB(ctx).Model(m).Select("*").Updates(arm).Error +} + +func (m *AlertMute) FE2DB() error { + idsBytes, err := json.Marshal(m.DatasourceIdsJson) + if err != nil { + return err + } + m.DatasourceIds = string(idsBytes) + + periodicMutesBytes, err := json.Marshal(m.PeriodicMutesJson) + if err != nil { + return err + } + m.PeriodicMutes = string(periodicMutesBytes) + + return nil +} + +func (m *AlertMute) DB2FE(ctx *ctx.Context) error { + json.Unmarshal([]byte(m.DatasourceIds), &m.DatasourceIdsJson) + err := json.Unmarshal([]byte(m.PeriodicMutes), &m.PeriodicMutesJson) + return err +} + +func (m *AlertMute) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(m).Updates(fields).Error +} + +func AlertMuteDel(ctx *ctx.Context, ids []int64) error { + if len(ids) == 0 { + return nil + } + return DB(ctx).Where("id in ?", ids).Delete(new(AlertMute)).Error +} + +func AlertMuteStatistics(ctx *ctx.Context) (*Statistics, error) { + // clean expired first + buf := int64(30) + err := DB(ctx).Where("etime < ? and mute_time_type = 0", time.Now().Unix()-buf).Delete(new(AlertMute)).Error + if err != nil { + return nil, err + } + + session := DB(ctx).Model(&AlertMute{}).Select("count(*) as total", "max(update_at) as last_updated") + + var stats []*Statistics + err = session.Find(&stats).Error + if err != nil { + return nil, err + } + + return stats[0], nil +} + +func AlertMuteGetsAll(ctx *ctx.Context) ([]*AlertMute, error) { + // get my cluster's mutes + session := DB(ctx).Model(&AlertMute{}) + + var lst []*AlertMute + err := session.Find(&lst).Error + if err != nil { + return nil, err + } + + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + + return lst, err +} + +func AlertMuteUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*AlertMute + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + var ids []int64 + if lst[i].Cluster == "$all" { + ids = append(ids, 0) + } else { + clusters := strings.Fields(lst[i].Cluster) + for j := 0; j < len(clusters); j++ { + if ds, exists := dsm[clusters[j]]; exists { + ids = append(ids, ds.Id) + } + } + } + + b, err := json.Marshal(ids) + if err != nil { + continue + } + lst[i].DatasourceIds = string(b) + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_ids": lst[i].DatasourceIds}) + if err != nil { + logger.Errorf("update alert rule:%d datasource ids failed, %v", lst[i].Id, err) + } + } + return nil +} diff --git a/models/alert_rule.go b/models/alert_rule.go new file mode 100644 index 0000000000000000000000000000000000000000..330eaab31fc5094abb05336bd90acf2abc765838 --- /dev/null +++ b/models/alert_rule.go @@ -0,0 +1,715 @@ +package models + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + + "github.com/pkg/errors" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/str" +) + +const ( + METRIC = "metric" + HOST = "host" + + PROMETHEUS = "prometheus" +) + +type AlertRule struct { + Id int64 `json:"id" gorm:"primaryKey"` + GroupId int64 `json:"group_id"` // busi group id + Cate string `json:"cate"` // alert rule cate (prometheus|elasticsearch) + DatasourceIds string `json:"-" gorm:"datasource_ids"` // datasource ids + DatasourceIdsJson []int64 `json:"datasource_ids" gorm:"-"` // for fe + Cluster string `json:"cluster"` // take effect by clusters, seperated by space + Name string `json:"name"` // rule name + Note string `json:"note"` // will sent in notify + Prod string `json:"prod"` // product empty means n9e + Algorithm string `json:"algorithm"` // algorithm (''|holtwinters), empty means threshold + AlgoParams string `json:"-" gorm:"algo_params"` // params algorithm need + AlgoParamsJson interface{} `json:"algo_params" gorm:"-"` // for fe + Delay int `json:"delay"` // Time (in seconds) to delay evaluation + Severity int `json:"severity"` // 1: Emergency 2: Warning 3: Notice + Disabled int `json:"disabled"` // 0: enabled, 1: disabled + PromForDuration int `json:"prom_for_duration"` // prometheus for, unit:s + PromQl string `json:"prom_ql"` // just one ql + RuleConfig string `json:"-" gorm:"rule_config"` // rule config + RuleConfigJson interface{} `json:"rule_config" gorm:"-"` // rule config for fe + PromEvalInterval int `json:"prom_eval_interval"` // unit:s + EnableStime string `json:"-"` // split by space: "00:00 10:00 12:00" + EnableStimeJSON string `json:"enable_stime" gorm:"-"` // for fe + EnableStimesJSON []string `json:"enable_stimes" gorm:"-"` // for fe + EnableEtime string `json:"-"` // split by space: "00:00 10:00 12:00" + EnableEtimeJSON string `json:"enable_etime" gorm:"-"` // for fe + EnableEtimesJSON []string `json:"enable_etimes" gorm:"-"` // for fe + EnableDaysOfWeek string `json:"-"` // eg: "0 1 2 3 4 5 6 ; 0 1 2" + EnableDaysOfWeekJSON []string `json:"enable_days_of_week" gorm:"-"` // for fe + EnableDaysOfWeeksJSON [][]string `json:"enable_days_of_weeks" gorm:"-"` // for fe + EnableInBG int `json:"enable_in_bg"` // 0: global 1: enable one busi-group + NotifyRecovered int `json:"notify_recovered"` // whether notify when recovery + NotifyChannels string `json:"-"` // split by space: sms voice email dingtalk wecom + NotifyChannelsJSON []string `json:"notify_channels" gorm:"-"` // for fe + NotifyGroups string `json:"-"` // split by space: 233 43 + NotifyGroupsObj []UserGroup `json:"notify_groups_obj" gorm:"-"` // for fe + NotifyGroupsJSON []string `json:"notify_groups" gorm:"-"` // for fe + NotifyRepeatStep int `json:"notify_repeat_step"` // notify repeat interval, unit: min + NotifyMaxNumber int `json:"notify_max_number"` // notify: max number + RecoverDuration int64 `json:"recover_duration"` // unit: s + Callbacks string `json:"-"` // split by space: http://a.com/api/x http://a.com/api/y' + CallbacksJSON []string `json:"callbacks" gorm:"-"` // for fe + RunbookUrl string `json:"runbook_url"` // sop url + AppendTags string `json:"-"` // split by space: service=n9e mod=api + AppendTagsJSON []string `json:"append_tags" gorm:"-"` // for fe + Annotations string `json:"-"` // + AnnotationsJSON map[string]string `json:"annotations" gorm:"-"` // for fe + CreateAt int64 `json:"create_at"` + CreateBy string `json:"create_by"` + UpdateAt int64 `json:"update_at"` + UpdateBy string `json:"update_by"` +} + +type PromRuleConfig struct { + Queries []PromQuery `json:"queries"` + Inhibit bool `json:"inhibit"` +} + +type HostRuleConfig struct { + Queries []HostQuery `json:"queries"` + Triggers []HostTrigger `json:"triggers"` + Inhibit bool `json:"inhibit"` +} + +type PromQuery struct { + PromQl string `json:"prom_ql"` + Severity int `json:"severity"` // 1: Emergency 2: Warning 3: Notice +} + +type HostTrigger struct { + Type string `json:"type"` + Duration int `json:"duration"` + Percent int `json:"percent"` + Severity int `json:"severity"` // 1: Emergency 2: Warning 3: Notice +} + +func GetHostsQuery(queries []HostQuery) map[string]interface{} { + var query = make(map[string]interface{}) + for _, q := range queries { + switch q.Key { + case "datasource_ids": + ids := ParseInt64(q.Values) + if q.Op == "==" { + query["datasource_id in (?)"] = ids + } else { + query["datasource_id not in (?)"] = ids + } + case "group_ids": + ids := ParseInt64(q.Values) + if q.Op == "==" { + query["group_id in (?)"] = ids + } else { + query["group_id not in (?)"] = ids + } + case "tags": + lst := []string{} + for _, v := range q.Values { + if v == nil { + continue + } + lst = append(lst, v.(string)) + } + if q.Op == "==" { + for _, tag := range lst { + query["tags like ?"] = "% " + tag + " %" + } + } else { + for _, tag := range lst { + query["tags not like ?"] = "% " + tag + " %" + } + } + case "idents": + lst := []string{} + for _, v := range q.Values { + if v == nil { + continue + } + lst = append(lst, v.(string)) + } + if q.Op == "==" { + query["ident in (?)"] = lst + } else { + query["ident not in (?)"] = lst + } + } + } + return query +} + +func ParseInt64(values []interface{}) []int64 { + b, _ := json.Marshal(values) + var ret []int64 + json.Unmarshal(b, &ret) + return ret +} + +type HostQuery struct { + Key string `json:"key"` + Op string `json:"op"` + Values []interface{} `json:"values"` +} + +func Str2Int(arr []string) []int64 { + var ret []int64 + for _, v := range arr { + i, _ := strconv.ParseInt(v, 10, 64) + ret = append(ret, i) + } + return ret +} + +func (ar *AlertRule) TableName() string { + return "alert_rule" +} + +func (ar *AlertRule) Verify(channelMap map[string]struct{}) error { + if ar.GroupId < 0 { + return fmt.Errorf("GroupId(%d) invalid", ar.GroupId) + } + + if IsAllDatasource(ar.DatasourceIdsJson) { + ar.DatasourceIdsJson = []int64{0} + } + + if str.Dangerous(ar.Name) { + return errors.New("Name has invalid characters") + } + + if ar.Name == "" { + return errors.New("name is blank") + } + + if ar.Prod == "" { + ar.Prod = METRIC + } + + if ar.Cate == "" { + ar.Cate = PROMETHEUS + } + + if ar.RuleConfig == "" { + return errors.New("rule_config is blank") + } + + if ar.PromEvalInterval <= 0 { + ar.PromEvalInterval = 15 + } + + // check in front-end + // if _, err := parser.ParseExpr(ar.PromQl); err != nil { + // return errors.New("prom_ql parse error: %") + // } + + ar.AppendTags = strings.TrimSpace(ar.AppendTags) + arr := strings.Fields(ar.AppendTags) + for i := 0; i < len(arr); i++ { + if len(strings.Split(arr[i], "=")) != 2 { + return fmt.Errorf("AppendTags(%s) invalid", arr[i]) + } + } + + gids := strings.Fields(ar.NotifyGroups) + for i := 0; i < len(gids); i++ { + if _, err := strconv.ParseInt(gids[i], 10, 64); err != nil { + return fmt.Errorf("NotifyGroups(%s) invalid", ar.NotifyGroups) + } + } + + channels := strings.Fields(ar.NotifyChannels) + if len(channels) > 0 { + nlst := make([]string, 0, len(channels)) + for i := 0; i < len(channels); i++ { + if _, ok := channelMap[channels[i]]; !ok { + continue + } + nlst = append(nlst, channels[i]) + } + ar.NotifyChannels = strings.Join(nlst, " ") + } else { + ar.NotifyChannels = "" + } + + return nil +} + +func (ar *AlertRule) Add(ctx *ctx.Context, channels map[string]struct{}) error { + if err := ar.Verify(channels); err != nil { + return err + } + + exists, err := AlertRuleExists(ctx, 0, ar.GroupId, ar.DatasourceIdsJson, ar.Name) + if err != nil { + return err + } + + if exists { + return errors.New("AlertRule already exists") + } + + now := time.Now().Unix() + ar.CreateAt = now + ar.UpdateAt = now + + return Insert(ctx, ar) +} + +func (ar *AlertRule) Update(ctx *ctx.Context, arf AlertRule, channelMap map[string]struct{}) error { + if ar.Name != arf.Name { + exists, err := AlertRuleExists(ctx, ar.Id, ar.GroupId, ar.DatasourceIdsJson, arf.Name) + if err != nil { + return err + } + + if exists { + return errors.New("AlertRule already exists") + } + } + + err := arf.FE2DB() + if err != nil { + return err + } + + arf.Id = ar.Id + arf.GroupId = ar.GroupId + arf.CreateAt = ar.CreateAt + arf.CreateBy = ar.CreateBy + arf.UpdateAt = time.Now().Unix() + + err = arf.Verify(channelMap) + if err != nil { + return err + } + return DB(ctx).Model(ar).Select("*").Updates(arf).Error +} + +func (ar *AlertRule) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(ar).Updates(fields).Error +} + +// for v5 rule +func (ar *AlertRule) FillDatasourceIds(ctx *ctx.Context) error { + if ar.DatasourceIds != "" { + json.Unmarshal([]byte(ar.DatasourceIds), &ar.DatasourceIdsJson) + return nil + } + return nil +} + +func (ar *AlertRule) FillNotifyGroups(ctx *ctx.Context, cache map[int64]*UserGroup) error { + // some user-group already deleted ? + count := len(ar.NotifyGroupsJSON) + if count == 0 { + ar.NotifyGroupsObj = []UserGroup{} + return nil + } + + exists := make([]string, 0, count) + delete := false + for i := range ar.NotifyGroupsJSON { + id, _ := strconv.ParseInt(ar.NotifyGroupsJSON[i], 10, 64) + + ug, has := cache[id] + if has { + exists = append(exists, ar.NotifyGroupsJSON[i]) + ar.NotifyGroupsObj = append(ar.NotifyGroupsObj, *ug) + continue + } + + ug, err := UserGroupGetById(ctx, id) + if err != nil { + return err + } + + if ug == nil { + delete = true + } else { + exists = append(exists, ar.NotifyGroupsJSON[i]) + ar.NotifyGroupsObj = append(ar.NotifyGroupsObj, *ug) + cache[id] = ug + } + } + + if delete { + // some user-group already deleted + ar.NotifyGroupsJSON = exists + ar.NotifyGroups = strings.Join(exists, " ") + DB(ctx).Model(ar).Update("notify_groups", ar.NotifyGroups) + } + + return nil +} + +func (ar *AlertRule) FE2DB() error { + + if len(ar.EnableStimesJSON) > 0 { + ar.EnableStime = strings.Join(ar.EnableStimesJSON, " ") + ar.EnableEtime = strings.Join(ar.EnableEtimesJSON, " ") + } else { + ar.EnableStime = ar.EnableStimeJSON + ar.EnableEtime = ar.EnableEtimeJSON + } + + if len(ar.EnableDaysOfWeeksJSON) > 0 { + for i := 0; i < len(ar.EnableDaysOfWeeksJSON); i++ { + if len(ar.EnableDaysOfWeeksJSON) == 1 { + ar.EnableDaysOfWeek = strings.Join(ar.EnableDaysOfWeeksJSON[i], " ") + } else { + if i == len(ar.EnableDaysOfWeeksJSON)-1 { + ar.EnableDaysOfWeek += strings.Join(ar.EnableDaysOfWeeksJSON[i], " ") + } else { + ar.EnableDaysOfWeek += strings.Join(ar.EnableDaysOfWeeksJSON[i], " ") + ";" + } + } + } + } else { + ar.EnableDaysOfWeek = strings.Join(ar.EnableDaysOfWeekJSON, " ") + } + + ar.NotifyChannels = strings.Join(ar.NotifyChannelsJSON, " ") + ar.NotifyGroups = strings.Join(ar.NotifyGroupsJSON, " ") + ar.Callbacks = strings.Join(ar.CallbacksJSON, " ") + ar.AppendTags = strings.Join(ar.AppendTagsJSON, " ") + algoParamsByte, err := json.Marshal(ar.AlgoParamsJson) + if err != nil { + return fmt.Errorf("marshal algo_params err:%v", err) + } + ar.AlgoParams = string(algoParamsByte) + + if len(ar.DatasourceIdsJson) > 0 { + idsByte, err := json.Marshal(ar.DatasourceIdsJson) + if err != nil { + return fmt.Errorf("marshal datasource_ids err:%v", err) + } + ar.DatasourceIds = string(idsByte) + } + + if ar.RuleConfigJson == nil { + query := PromQuery{ + PromQl: ar.PromQl, + Severity: ar.Severity, + } + ar.RuleConfigJson = PromRuleConfig{ + Queries: []PromQuery{query}, + } + } + + // json.Marshal RuleConfigJson + if ar.RuleConfigJson != nil { + b, err := json.Marshal(ar.RuleConfigJson) + if err != nil { + return fmt.Errorf("marshal rule_config err:%v", err) + } + ar.RuleConfig = string(b) + } + + if ar.AnnotationsJSON != nil { + b, err := json.Marshal(ar.AnnotationsJSON) + if err != nil { + return fmt.Errorf("marshal annotations err:%v", err) + } + ar.Annotations = string(b) + } + + return nil +} + +func (ar *AlertRule) DB2FE(ctx *ctx.Context) error { + ar.EnableStimesJSON = strings.Fields(ar.EnableStime) + ar.EnableEtimesJSON = strings.Fields(ar.EnableEtime) + if len(ar.EnableEtimesJSON) > 0 { + ar.EnableStimeJSON = ar.EnableStimesJSON[0] + ar.EnableEtimeJSON = ar.EnableEtimesJSON[0] + } + + cache := strings.Split(ar.EnableDaysOfWeek, ";") + for i := 0; i < len(cache); i++ { + ar.EnableDaysOfWeeksJSON = append(ar.EnableDaysOfWeeksJSON, strings.Fields(cache[i])) + } + if len(ar.EnableDaysOfWeeksJSON) > 0 { + ar.EnableDaysOfWeekJSON = ar.EnableDaysOfWeeksJSON[0] + } + + ar.NotifyChannelsJSON = strings.Fields(ar.NotifyChannels) + ar.NotifyGroupsJSON = strings.Fields(ar.NotifyGroups) + ar.CallbacksJSON = strings.Fields(ar.Callbacks) + ar.AppendTagsJSON = strings.Fields(ar.AppendTags) + json.Unmarshal([]byte(ar.AlgoParams), &ar.AlgoParamsJson) + json.Unmarshal([]byte(ar.RuleConfig), &ar.RuleConfigJson) + json.Unmarshal([]byte(ar.Annotations), &ar.AnnotationsJSON) + + err := ar.FillDatasourceIds(ctx) + return err +} + +func AlertRuleDels(ctx *ctx.Context, ids []int64, bgid ...int64) error { + for i := 0; i < len(ids); i++ { + session := DB(ctx).Where("id = ?", ids[i]) + if len(bgid) > 0 { + session = session.Where("group_id = ?", bgid[0]) + } + ret := session.Delete(&AlertRule{}) + if ret.Error != nil { + return ret.Error + } + + // 说明确实删掉了,把相关的活跃告警也删了,这些告警永远都不会恢复了,而且策略都没了,说明没人关心了 + if ret.RowsAffected > 0 { + DB(ctx).Where("rule_id = ?", ids[i]).Delete(new(AlertCurEvent)) + } + } + + return nil +} + +func AlertRuleExists(ctx *ctx.Context, id, groupId int64, datasourceIds []int64, name string) (bool, error) { + session := DB(ctx).Where("id <> ? and group_id = ? and name = ?", id, groupId, name) + + var lst []AlertRule + err := session.Find(&lst).Error + if err != nil { + return false, err + } + if len(lst) == 0 { + return false, nil + } + + // match cluster + for _, r := range lst { + r.FillDatasourceIds(ctx) + for _, id := range r.DatasourceIdsJson { + if MatchDatasource(datasourceIds, id) { + return true, nil + } + } + } + return false, nil +} + +func AlertRuleGets(ctx *ctx.Context, groupId int64) ([]AlertRule, error) { + session := DB(ctx).Where("group_id=?", groupId).Order("name") + + var lst []AlertRule + err := session.Find(&lst).Error + if err == nil { + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + } + + return lst, err +} + +func AlertRuleGetsAll(ctx *ctx.Context) ([]*AlertRule, error) { + session := DB(ctx).Where("disabled = ? and (prod = ? or prod = ? or prod = ?)", 0, "", HOST, METRIC) + + var lst []*AlertRule + err := session.Find(&lst).Error + if err != nil { + return lst, err + } + + if len(lst) == 0 { + return lst, nil + } + + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + return lst, nil +} + +func AlertRulesGetsBy(ctx *ctx.Context, prods []string, query, algorithm, cluster string, cates []string, disabled int) ([]*AlertRule, error) { + session := DB(ctx).Where("prod in (?)", prods) + + if query != "" { + arr := strings.Fields(query) + for i := 0; i < len(arr); i++ { + qarg := "%" + arr[i] + "%" + session = session.Where("append_tags like ?", qarg) + } + } + + if algorithm != "" { + session = session.Where("algorithm = ?", algorithm) + } + + if cluster != "" { + session = session.Where("cluster like ?", "%"+cluster+"%") + } + + if len(cates) != 0 { + session = session.Where("cate in (?)", cates) + } + + if disabled != -1 { + session = session.Where("disabled = ?", disabled) + } + + var lst []*AlertRule + err := session.Find(&lst).Error + if err == nil { + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + } + + return lst, err +} + +func AlertRuleGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertRule, error) { + var lst []*AlertRule + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + lst[0].DB2FE(ctx) + + return lst[0], nil +} + +func AlertRuleGetById(ctx *ctx.Context, id int64) (*AlertRule, error) { + return AlertRuleGet(ctx, "id=?", id) +} + +func AlertRuleGetName(ctx *ctx.Context, id int64) (string, error) { + var names []string + err := DB(ctx).Model(new(AlertRule)).Where("id = ?", id).Pluck("name", &names).Error + if err != nil { + return "", err + } + + if len(names) == 0 { + return "", nil + } + + return names[0], nil +} + +func AlertRuleStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&AlertRule{}).Select("count(*) as total", "max(update_at) as last_updated").Where("disabled = ? and (prod = ? or prod = ? or prod = ?)", 0, "", HOST, METRIC) + + var stats []*Statistics + err := session.Find(&stats).Error + if err != nil { + return nil, err + } + + return stats[0], nil +} + +func (ar *AlertRule) IsPrometheusRule() bool { + return ar.Algorithm == "" && (ar.Cate == "" || strings.ToLower(ar.Cate) == PROMETHEUS) +} + +func (ar *AlertRule) IsHostRule() bool { + return ar.Prod == HOST +} + +func (ar *AlertRule) GetRuleType() string { + if ar.Prod == METRIC { + return ar.Cate + } + + return ar.Prod +} + +func (ar *AlertRule) GenerateNewEvent(ctx *ctx.Context) *AlertCurEvent { + event := &AlertCurEvent{} + ar.UpdateEvent(event) + return event +} + +func (ar *AlertRule) UpdateEvent(event *AlertCurEvent) { + if event == nil { + return + } + + event.GroupId = ar.GroupId + event.Cate = ar.Cate + event.RuleId = ar.Id + event.RuleName = ar.Name + event.RuleNote = ar.Note + event.RuleProd = ar.Prod + event.RuleAlgo = ar.Algorithm + event.PromForDuration = ar.PromForDuration + event.PromQl = ar.PromQl + event.RuleConfig = ar.RuleConfig + event.RuleConfigJson = ar.RuleConfigJson + event.PromEvalInterval = ar.PromEvalInterval + event.Callbacks = ar.Callbacks + event.CallbacksJSON = ar.CallbacksJSON + event.RunbookUrl = ar.RunbookUrl + event.NotifyRecovered = ar.NotifyRecovered + event.NotifyChannels = ar.NotifyChannels + event.NotifyChannelsJSON = ar.NotifyChannelsJSON + event.NotifyGroups = ar.NotifyGroups + event.NotifyGroupsJSON = ar.NotifyGroupsJSON + event.AnnotationsJSON = ar.AnnotationsJSON +} + +func AlertRuleUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*AlertRule + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + var ids []int64 + if lst[i].Cluster == "$all" { + ids = append(ids, 0) + } else { + clusters := strings.Fields(lst[i].Cluster) + for j := 0; j < len(clusters); j++ { + if ds, exists := dsm[clusters[j]]; exists { + ids = append(ids, ds.Id) + } + } + } + + b, err := json.Marshal(ids) + if err != nil { + continue + } + lst[i].DatasourceIds = string(b) + + m := make(map[string]string) + if lst[i].RunbookUrl != "" { + m["runbook_url"] = lst[i].RunbookUrl + + b, err = json.Marshal(m) + if err != nil { + continue + } + + lst[i].Annotations = string(b) + } + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_ids": lst[i].DatasourceIds, "annotations": lst[i].Annotations}) + if err != nil { + logger.Errorf("update alert rule:%d datasource ids failed, %v", lst[i].Id, err) + } + + } + return nil +} diff --git a/models/alert_subscribe.go b/models/alert_subscribe.go new file mode 100644 index 0000000000000000000000000000000000000000..b9fc5005584d8c179e13bc5b7889839c6a3168c9 --- /dev/null +++ b/models/alert_subscribe.go @@ -0,0 +1,354 @@ +package models + +import ( + "encoding/json" + "regexp" + "strconv" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/pkg/errors" + "github.com/toolkits/pkg/logger" +) + +type AlertSubscribe struct { + Id int64 `json:"id" gorm:"primaryKey"` + Name string `json:"name"` // AlertSubscribe name + Disabled int `json:"disabled"` // 0: enabled, 1: disabled + GroupId int64 `json:"group_id"` + Prod string `json:"prod"` + Cate string `json:"cate"` + DatasourceIds string `json:"-" gorm:"datasource_ids"` // datasource ids + DatasourceIdsJson []int64 `json:"datasource_ids" gorm:"-"` // for fe + Cluster string `json:"cluster"` // take effect by clusters, seperated by space + RuleId int64 `json:"rule_id"` + ForDuration int64 `json:"for_duration"` // for duration, unit: second + RuleName string `json:"rule_name" gorm:"-"` // for fe + Tags ormx.JSONArr `json:"tags"` + RedefineSeverity int `json:"redefine_severity"` + NewSeverity int `json:"new_severity"` + RedefineChannels int `json:"redefine_channels"` + NewChannels string `json:"new_channels"` + UserGroupIds string `json:"user_group_ids"` + UserGroups []UserGroup `json:"user_groups" gorm:"-"` // for fe + RedefineWebhooks int `json:"redefine_webhooks"` + Webhooks string `json:"-" gorm:"webhooks"` + WebhooksJson []string `json:"webhooks" gorm:"-"` + CreateBy string `json:"create_by"` + CreateAt int64 `json:"create_at"` + UpdateBy string `json:"update_by"` + UpdateAt int64 `json:"update_at"` + ITags []TagFilter `json:"-" gorm:"-"` // inner tags +} + +func (s *AlertSubscribe) TableName() string { + return "alert_subscribe" +} + +func AlertSubscribeGets(ctx *ctx.Context, groupId int64) (lst []AlertSubscribe, err error) { + err = DB(ctx).Where("group_id=?", groupId).Order("id desc").Find(&lst).Error + return +} + +func AlertSubscribeGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertSubscribe, error) { + var lst []*AlertSubscribe + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + return lst[0], nil +} + +func (s *AlertSubscribe) IsDisabled() bool { + return s.Disabled == 1 +} + +func (s *AlertSubscribe) Verify() error { + if IsAllDatasource(s.DatasourceIdsJson) { + s.DatasourceIdsJson = []int64{0} + } + + if err := s.Parse(); err != nil { + return err + } + + if len(s.ITags) == 0 && s.RuleId == 0 { + return errors.New("rule_id and tags are both blank") + } + + ugids := strings.Fields(s.UserGroupIds) + for i := 0; i < len(ugids); i++ { + if _, err := strconv.ParseInt(ugids[i], 10, 64); err != nil { + return errors.New("user_group_ids invalid") + } + } + + return nil +} + +func (s *AlertSubscribe) FE2DB() error { + idsByte, err := json.Marshal(s.DatasourceIdsJson) + if err != nil { + return err + } + s.DatasourceIds = string(idsByte) + + if len(s.WebhooksJson) > 0 { + b, _ := json.Marshal(s.WebhooksJson) + s.Webhooks = string(b) + } + + return nil +} + +func (s *AlertSubscribe) DB2FE() error { + if s.DatasourceIds != "" { + if err := json.Unmarshal([]byte(s.DatasourceIds), &s.DatasourceIdsJson); err != nil { + return err + } + } + + if s.Webhooks != "" { + if err := json.Unmarshal([]byte(s.Webhooks), &s.WebhooksJson); err != nil { + return err + } + } + return nil +} + +func (s *AlertSubscribe) Parse() error { + err := json.Unmarshal(s.Tags, &s.ITags) + if err != nil { + return err + } + + for i := 0; i < len(s.ITags); i++ { + if s.ITags[i].Func == "=~" || s.ITags[i].Func == "!~" { + s.ITags[i].Regexp, err = regexp.Compile(s.ITags[i].Value) + if err != nil { + return err + } + } else if s.ITags[i].Func == "in" || s.ITags[i].Func == "not in" { + arr := strings.Fields(s.ITags[i].Value) + s.ITags[i].Vset = make(map[string]struct{}) + for j := 0; j < len(arr); j++ { + s.ITags[i].Vset[arr[j]] = struct{}{} + } + } + } + + return err +} + +func (s *AlertSubscribe) Add(ctx *ctx.Context) error { + if err := s.Verify(); err != nil { + return err + } + + if err := s.FE2DB(); err != nil { + return err + } + + now := time.Now().Unix() + s.CreateAt = now + s.UpdateAt = now + return Insert(ctx, s) +} + +func (s *AlertSubscribe) FillRuleName(ctx *ctx.Context, cache map[int64]string) error { + if s.RuleId <= 0 { + s.RuleName = "" + return nil + } + + name, has := cache[s.RuleId] + if has { + s.RuleName = name + return nil + } + + name, err := AlertRuleGetName(ctx, s.RuleId) + if err != nil { + return err + } + + if name == "" { + name = "Error: AlertRule not found" + } + + s.RuleName = name + cache[s.RuleId] = name + return nil +} + +// for v5 rule +func (s *AlertSubscribe) FillDatasourceIds(ctx *ctx.Context) error { + if s.DatasourceIds != "" { + json.Unmarshal([]byte(s.DatasourceIds), &s.DatasourceIdsJson) + return nil + } + return nil +} + +func (s *AlertSubscribe) FillUserGroups(ctx *ctx.Context, cache map[int64]*UserGroup) error { + // some user-group already deleted ? + ugids := strings.Fields(s.UserGroupIds) + + count := len(ugids) + if count == 0 { + s.UserGroups = []UserGroup{} + return nil + } + + exists := make([]string, 0, count) + delete := false + for i := range ugids { + id, _ := strconv.ParseInt(ugids[i], 10, 64) + + ug, has := cache[id] + if has { + exists = append(exists, ugids[i]) + s.UserGroups = append(s.UserGroups, *ug) + continue + } + + ug, err := UserGroupGetById(ctx, id) + if err != nil { + return err + } + + if ug == nil { + delete = true + } else { + exists = append(exists, ugids[i]) + s.UserGroups = append(s.UserGroups, *ug) + cache[id] = ug + } + } + + if delete { + // some user-group already deleted + DB(ctx).Model(s).Update("user_group_ids", strings.Join(exists, " ")) + s.UserGroupIds = strings.Join(exists, " ") + } + + return nil +} + +func (s *AlertSubscribe) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { + if err := s.Verify(); err != nil { + return err + } + + if err := s.FE2DB(); err != nil { + return err + } + + return DB(ctx).Model(s).Select(selectField, selectFields...).Updates(s).Error +} + +func AlertSubscribeDel(ctx *ctx.Context, ids []int64) error { + if len(ids) == 0 { + return nil + } + return DB(ctx).Where("id in ?", ids).Delete(new(AlertSubscribe)).Error +} + +func AlertSubscribeStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&AlertSubscribe{}).Select("count(*) as total", "max(update_at) as last_updated") + + var stats []*Statistics + err := session.Find(&stats).Error + if err != nil { + return nil, err + } + + return stats[0], nil +} + +func AlertSubscribeGetsAll(ctx *ctx.Context) ([]*AlertSubscribe, error) { + // get my cluster's subscribes + session := DB(ctx).Model(&AlertSubscribe{}) + + var lst []*AlertSubscribe + err := session.Find(&lst).Error + return lst, err +} + +func (s *AlertSubscribe) MatchCluster(dsId int64) bool { + // 没有配置数据源, 或者事件不需要关联数据源 + // do not match any datasource or event not related to datasource + if len(s.DatasourceIdsJson) == 0 || dsId == 0 { + return true + } + + for _, id := range s.DatasourceIdsJson { + if id == dsId || id == 0 { + return true + } + } + return false +} + +func (s *AlertSubscribe) ModifyEvent(event *AlertCurEvent) { + if s.RedefineSeverity == 1 { + event.Severity = s.NewSeverity + } + + if s.RedefineChannels == 1 { + event.NotifyChannels = s.NewChannels + event.NotifyChannelsJSON = strings.Fields(s.NewChannels) + } + + if s.RedefineWebhooks == 1 { + event.Callbacks = s.Webhooks + event.CallbacksJSON = s.WebhooksJson + } + + event.NotifyGroups = s.UserGroupIds + event.NotifyGroupsJSON = strings.Fields(s.UserGroupIds) +} + +func (s *AlertSubscribe) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(s).Updates(fields).Error +} + +func AlertSubscribeUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*AlertSubscribe + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + var ids []int64 + if lst[i].Cluster == "$all" { + ids = append(ids, 0) + } else { + clusters := strings.Fields(lst[i].Cluster) + for j := 0; j < len(clusters); j++ { + if ds, exists := dsm[clusters[j]]; exists { + ids = append(ids, ds.Id) + } + } + } + b, err := json.Marshal(ids) + if err != nil { + continue + } + lst[i].DatasourceIds = string(b) + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_ids": lst[i].DatasourceIds}) + if err != nil { + logger.Errorf("update alert rule:%d datasource ids failed, %v", lst[i].Id, err) + } + } + return nil +} diff --git a/models/alerting_engine.go b/models/alerting_engine.go new file mode 100644 index 0000000000000000000000000000000000000000..6b7c2d17f8ce17f052249e4c5227fae56b6ac03d --- /dev/null +++ b/models/alerting_engine.go @@ -0,0 +1,153 @@ +package models + +import ( + "fmt" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" +) + +type AlertingEngines struct { + Id int64 `json:"id" gorm:"primaryKey"` + Instance string `json:"instance"` + Cluster string `json:"cluster"` + DatasourceId int64 `json:"datasource_id"` + Clock int64 `json:"clock"` +} + +func (e *AlertingEngines) TableName() string { + return "alerting_engines" +} + +// UpdateCluster 页面上用户会给各个n9e-server分配要关联的目标集群是什么 +func (e *AlertingEngines) UpdateDatasourceId(ctx *ctx.Context, id int64) error { + count, err := Count(DB(ctx).Model(&AlertingEngines{}).Where("id<>? and instance=? and datasource_id=?", e.Id, e.Instance, id)) + if err != nil { + return err + } + + if count > 0 { + return fmt.Errorf("instance %s and datasource_id %d already exists", e.Instance, id) + } + + e.DatasourceId = id + return DB(ctx).Model(e).Select("datasource_id").Updates(e).Error +} + +func AlertingEngineAdd(ctx *ctx.Context, instance string, datasourceId int64) error { + count, err := Count(DB(ctx).Model(&AlertingEngines{}).Where("instance=? and datasource_id=?", instance, datasourceId)) + if err != nil { + return err + } + + if count > 0 { + return fmt.Errorf("instance %s and datasource_id %d already exists", instance, datasourceId) + } + + err = DB(ctx).Create(&AlertingEngines{ + Instance: instance, + DatasourceId: datasourceId, + Clock: time.Now().Unix(), + }).Error + + return err +} + +func AlertingEngineDel(ctx *ctx.Context, ids []int64) error { + if len(ids) == 0 { + return nil + } + return DB(ctx).Where("id in ?", ids).Delete(new(AlertingEngines)).Error +} + +func AlertingEngineGetDatasourceIds(ctx *ctx.Context, instance string) ([]int64, error) { + var objs []AlertingEngines + err := DB(ctx).Where("instance=?", instance).Find(&objs).Error + if err != nil { + return []int64{}, err + } + + if len(objs) == 0 { + return []int64{}, nil + } + var ids []int64 + for i := 0; i < len(objs); i++ { + ids = append(ids, objs[i].DatasourceId) + } + + return ids, nil +} + +// AlertingEngineGets 拉取列表数据,用户要在页面上看到所有 n9e-server 实例列表,然后为其分配 cluster +func AlertingEngineGets(ctx *ctx.Context, where string, args ...interface{}) ([]*AlertingEngines, error) { + var objs []*AlertingEngines + var err error + session := DB(ctx).Order("instance") + if where == "" { + err = session.Find(&objs).Error + } else { + err = session.Where(where, args...).Find(&objs).Error + } + return objs, err +} + +func AlertingEngineGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertingEngines, error) { + lst, err := AlertingEngineGets(ctx, where, args...) + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + return lst[0], nil +} + +func AlertingEngineGetsClusters(ctx *ctx.Context, where string, args ...interface{}) ([]string, error) { + var arr []string + var err error + session := DB(ctx).Model(new(AlertingEngines)).Where("cluster != ''").Order("cluster").Distinct("cluster") + if where == "" { + err = session.Pluck("cluster", &arr).Error + } else { + err = session.Where(where, args...).Pluck("cluster", &arr).Error + } + return arr, err +} + +func AlertingEngineGetsInstances(ctx *ctx.Context, where string, args ...interface{}) ([]string, error) { + var arr []string + var err error + session := DB(ctx).Model(new(AlertingEngines)).Order("instance") + if where == "" { + err = session.Pluck("instance", &arr).Error + } else { + err = session.Where(where, args...).Pluck("instance", &arr).Error + } + return arr, err +} + +func AlertingEngineHeartbeatWithCluster(ctx *ctx.Context, instance, cluster string, datasourceId int64) error { + var total int64 + err := DB(ctx).Model(new(AlertingEngines)).Where("instance=? and cluster = ? and datasource_id=?", instance, cluster, datasourceId).Count(&total).Error + if err != nil { + return err + } + + if total == 0 { + // insert + err = DB(ctx).Create(&AlertingEngines{ + Instance: instance, + DatasourceId: datasourceId, + Cluster: cluster, + Clock: time.Now().Unix(), + }).Error + } else { + // updates + fields := map[string]interface{}{"clock": time.Now().Unix()} + err = DB(ctx).Model(new(AlertingEngines)).Where("instance=? and cluster = ? and datasource_id=?", instance, cluster, datasourceId).Updates(fields).Error + } + + return err +} diff --git a/models/board.go b/models/board.go new file mode 100644 index 0000000000000000000000000000000000000000..d48205ea99e82a5205cfeb1660e314300f538971 --- /dev/null +++ b/models/board.go @@ -0,0 +1,206 @@ +package models + +import ( + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" + "github.com/toolkits/pkg/str" + "gorm.io/gorm" +) + +type Board struct { + Id int64 `json:"id" gorm:"primaryKey"` + GroupId int64 `json:"group_id"` + Name string `json:"name"` + Ident string `json:"ident"` + Tags string `json:"tags"` + CreateAt int64 `json:"create_at"` + CreateBy string `json:"create_by"` + UpdateAt int64 `json:"update_at"` + UpdateBy string `json:"update_by"` + Configs string `json:"configs" gorm:"-"` + Public int `json:"public"` // 0: false, 1: true + BuiltIn int `json:"built_in"` // 0: false, 1: true + Hide int `json:"hide"` // 0: false, 1: true +} + +func (b *Board) TableName() string { + return "board" +} + +func (b *Board) Verify() error { + if b.Name == "" { + return errors.New("Name is blank") + } + + if str.Dangerous(b.Name) { + return errors.New("Name has invalid characters") + } + + return nil +} + +func (b *Board) CanRenameIdent(ctx *ctx.Context, ident string) (bool, error) { + if ident == "" { + return true, nil + } + + cnt, err := Count(DB(ctx).Model(b).Where("ident=? and id <> ?", ident, b.Id)) + if err != nil { + return false, err + } + + return cnt == 0, nil +} + +func (b *Board) Add(ctx *ctx.Context) error { + if err := b.Verify(); err != nil { + return err + } + + if b.Ident != "" { + // ident duplicate check + cnt, err := Count(DB(ctx).Model(b).Where("ident=?", b.Ident)) + if err != nil { + return err + } + + if cnt > 0 { + return errors.New("Ident duplicate") + } + } + + now := time.Now().Unix() + b.CreateAt = now + b.UpdateAt = now + + return Insert(ctx, b) +} + +func (b *Board) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { + if err := b.Verify(); err != nil { + return err + } + + return DB(ctx).Model(b).Select(selectField, selectFields...).Updates(b).Error +} + +func (b *Board) Del(ctx *ctx.Context) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("id=?", b.Id).Delete(&BoardPayload{}).Error; err != nil { + return err + } + + if err := tx.Where("id=?", b.Id).Delete(&Board{}).Error; err != nil { + return err + } + + return nil + }) +} + +func BoardGetByID(ctx *ctx.Context, id int64) (*Board, error) { + var lst []*Board + err := DB(ctx).Where("id = ?", id).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + return lst[0], nil +} + +// BoardGet for detail page +func BoardGet(ctx *ctx.Context, where string, args ...interface{}) (*Board, error) { + var lst []*Board + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + payload, err := BoardPayloadGet(ctx, lst[0].Id) + if err != nil { + return nil, err + } + + lst[0].Configs = payload + + return lst[0], nil +} + +func BoardCount(ctx *ctx.Context, where string, args ...interface{}) (num int64, err error) { + return Count(DB(ctx).Model(&Board{}).Where(where, args...)) +} + +func BoardExists(ctx *ctx.Context, where string, args ...interface{}) (bool, error) { + num, err := BoardCount(ctx, where, args...) + return num > 0, err +} + +// BoardGets for list page +func BoardGetsByGroupId(ctx *ctx.Context, groupId int64, query string) ([]Board, error) { + session := DB(ctx).Where("group_id=?", groupId).Order("name") + + arr := strings.Fields(query) + if len(arr) > 0 { + for i := 0; i < len(arr); i++ { + if strings.HasPrefix(arr[i], "-") { + q := "%" + arr[i][1:] + "%" + session = session.Where("name not like ? and tags not like ?", q, q) + } else { + q := "%" + arr[i] + "%" + session = session.Where("(name like ? or tags like ?)", q, q) + } + } + } + + var objs []Board + err := session.Find(&objs).Error + return objs, err +} + +func BoardGets(ctx *ctx.Context, query, where string, args ...interface{}) ([]Board, error) { + session := DB(ctx).Order("name") + if where != "" { + session = session.Where(where, args...) + } + + arr := strings.Fields(query) + if len(arr) > 0 { + for i := 0; i < len(arr); i++ { + if strings.HasPrefix(arr[i], "-") { + q := "%" + arr[i][1:] + "%" + session = session.Where("name not like ? and tags not like ?", q, q) + } else { + q := "%" + arr[i] + "%" + session = session.Where("(name like ? or tags like ?)", q, q) + } + } + } + + var objs []Board + err := session.Find(&objs).Error + return objs, err +} + +func BoardSetHide(ctx *ctx.Context, ids []int64) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&Board{}).Where("built_in = 1").Update("hide", 0).Error; err != nil { + return err + } + + if err := tx.Model(&Board{}).Where("id in (?) and built_in = 1", ids).Update("hide", 1).Error; err != nil { + return err + } + return nil + }) +} diff --git a/models/board_payload.go b/models/board_payload.go new file mode 100644 index 0000000000000000000000000000000000000000..ada23ec603e1a445c3d49e2cb94f9cba334fb472 --- /dev/null +++ b/models/board_payload.go @@ -0,0 +1,62 @@ +package models + +import ( + "errors" + + "github.com/ccfos/nightingale/v6/pkg/ctx" +) + +type BoardPayload struct { + Id int64 `json:"id" gorm:"primaryKey"` + Payload string `json:"payload"` +} + +func (p *BoardPayload) TableName() string { + return "board_payload" +} + +func (p *BoardPayload) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { + return DB(ctx).Model(p).Select(selectField, selectFields...).Updates(p).Error +} + +func BoardPayloadGets(ctx *ctx.Context, ids []int64) ([]*BoardPayload, error) { + if len(ids) == 0 { + return nil, errors.New("empty ids") + } + + var arr []*BoardPayload + err := DB(ctx).Where("id in ?", ids).Find(&arr).Error + return arr, err +} + +func BoardPayloadGet(ctx *ctx.Context, id int64) (string, error) { + payloads, err := BoardPayloadGets(ctx, []int64{id}) + if err != nil { + return "", err + } + + if len(payloads) == 0 { + return "", nil + } + + return payloads[0].Payload, nil +} + +func BoardPayloadSave(ctx *ctx.Context, id int64, payload string) error { + var bp BoardPayload + err := DB(ctx).Where("id = ?", id).Find(&bp).Error + if err != nil { + return err + } + + if bp.Id > 0 { + // already exists + bp.Payload = payload + return bp.Update(ctx, "payload") + } + + return Insert(ctx, &BoardPayload{ + Id: id, + Payload: payload, + }) +} diff --git a/models/builtin_cate.go b/models/builtin_cate.go new file mode 100644 index 0000000000000000000000000000000000000000..3eb36d73a3b3add753a5644b57667584109f437a --- /dev/null +++ b/models/builtin_cate.go @@ -0,0 +1,37 @@ +package models + +import ( + "github.com/ccfos/nightingale/v6/pkg/ctx" +) + +type BuiltinCate struct { + Id int64 `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + UserId int64 `json:"user_id"` +} + +func (b *BuiltinCate) TableName() string { + return "builtin_cate" +} + +// 创建 builtin_cate +func (b *BuiltinCate) Create(c *ctx.Context) error { + return Insert(c, b) +} + +// 删除 builtin_cate +func BuiltinCateDelete(c *ctx.Context, name string, userId int64) error { + return DB(c).Where("name=? and user_id=?", name, userId).Delete(&BuiltinCate{}).Error +} + +// 根据 userId 获取 builtin_cate +func BuiltinCateGetByUserId(c *ctx.Context, userId int64) (map[string]BuiltinCate, error) { + var builtinCates []BuiltinCate + err := DB(c).Where("user_id=?", userId).Find(&builtinCates).Error + var builtinCatesMap = make(map[string]BuiltinCate) + for _, builtinCate := range builtinCates { + builtinCatesMap[builtinCate.Name] = builtinCate + } + + return builtinCatesMap, err +} diff --git a/src/models/busi_group.go b/models/busi_group.go similarity index 62% rename from src/models/busi_group.go rename to models/busi_group.go index 7f9389be6cb36cacffadcaab9f91ba5a93e4fb00..8a307096b64b3004ec634ca7e6fbaf628c0d2793 100644 --- a/src/models/busi_group.go +++ b/models/busi_group.go @@ -4,6 +4,8 @@ import ( "fmt" "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "gorm.io/gorm" ) @@ -18,6 +20,13 @@ type BusiGroup struct { UpdateAt int64 `json:"update_at"` UpdateBy string `json:"update_by"` UserGroups []UserGroupWithPermFlag `json:"user_groups" gorm:"-"` + DB *gorm.DB `json:"-" gorm:"-"` +} + +func New(db *gorm.DB) *BusiGroup { + return &BusiGroup{ + DB: db, + } } type UserGroupWithPermFlag struct { @@ -29,8 +38,8 @@ func (bg *BusiGroup) TableName() string { return "busi_group" } -func (bg *BusiGroup) FillUserGroups() error { - members, err := BusiGroupMemberGetsByBusiGroupId(bg.Id) +func (bg *BusiGroup) FillUserGroups(ctx *ctx.Context) error { + members, err := BusiGroupMemberGetsByBusiGroupId(ctx, bg.Id) if err != nil { return err } @@ -40,7 +49,7 @@ func (bg *BusiGroup) FillUserGroups() error { } for i := 0; i < len(members); i++ { - ug, err := UserGroupGetById(members[i].UserGroupId) + ug, err := UserGroupGetById(ctx, members[i].UserGroupId) if err != nil { return err } @@ -53,9 +62,9 @@ func (bg *BusiGroup) FillUserGroups() error { return nil } -func BusiGroupGetMap() (map[int64]*BusiGroup, error) { +func BusiGroupGetMap(ctx *ctx.Context) (map[int64]*BusiGroup, error) { var lst []*BusiGroup - err := DB().Find(&lst).Error + err := DB(ctx).Find(&lst).Error if err != nil { return nil, err } @@ -68,9 +77,9 @@ func BusiGroupGetMap() (map[int64]*BusiGroup, error) { return ret, nil } -func BusiGroupGet(where string, args ...interface{}) (*BusiGroup, error) { +func BusiGroupGet(ctx *ctx.Context, where string, args ...interface{}) (*BusiGroup, error) { var lst []*BusiGroup - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } @@ -82,17 +91,17 @@ func BusiGroupGet(where string, args ...interface{}) (*BusiGroup, error) { return lst[0], nil } -func BusiGroupGetById(id int64) (*BusiGroup, error) { - return BusiGroupGet("id=?", id) +func BusiGroupGetById(ctx *ctx.Context, id int64) (*BusiGroup, error) { + return BusiGroupGet(ctx, "id=?", id) } -func BusiGroupExists(where string, args ...interface{}) (bool, error) { - num, err := Count(DB().Model(&BusiGroup{}).Where(where, args...)) +func BusiGroupExists(ctx *ctx.Context, where string, args ...interface{}) (bool, error) { + num, err := Count(DB(ctx).Model(&BusiGroup{}).Where(where, args...)) return num > 0, err } -func (bg *BusiGroup) Del() error { - has, err := Exists(DB().Model(&AlertMute{}).Where("group_id=?", bg.Id)) +func (bg *BusiGroup) Del(ctx *ctx.Context) error { + has, err := Exists(DB(ctx).Model(&AlertMute{}).Where("group_id=?", bg.Id)) if err != nil { return err } @@ -101,7 +110,7 @@ func (bg *BusiGroup) Del() error { return errors.New("Some alert mutes still in the BusiGroup") } - has, err = Exists(DB().Model(&AlertSubscribe{}).Where("group_id=?", bg.Id)) + has, err = Exists(DB(ctx).Model(&AlertSubscribe{}).Where("group_id=?", bg.Id)) if err != nil { return err } @@ -110,7 +119,7 @@ func (bg *BusiGroup) Del() error { return errors.New("Some alert subscribes still in the BusiGroup") } - has, err = Exists(DB().Model(&Target{}).Where("group_id=?", bg.Id)) + has, err = Exists(DB(ctx).Model(&Target{}).Where("group_id=?", bg.Id)) if err != nil { return err } @@ -119,7 +128,7 @@ func (bg *BusiGroup) Del() error { return errors.New("Some targets still in the BusiGroup") } - has, err = Exists(DB().Model(&Board{}).Where("group_id=?", bg.Id)) + has, err = Exists(DB(ctx).Model(&Board{}).Where("group_id=?", bg.Id)) if err != nil { return err } @@ -128,7 +137,7 @@ func (bg *BusiGroup) Del() error { return errors.New("Some dashboards still in the BusiGroup") } - has, err = Exists(DB().Model(&TaskTpl{}).Where("group_id=?", bg.Id)) + has, err = Exists(DB(ctx).Model(&TaskTpl{}).Where("group_id=?", bg.Id)) if err != nil { return err } @@ -137,7 +146,7 @@ func (bg *BusiGroup) Del() error { return errors.New("Some recovery scripts still in the BusiGroup") } - // hasCR, err := Exists(DB().Table("collect_rule").Where("group_id=?", bg.Id)) + // hasCR, err := Exists(DB(ctx).Table("collect_rule").Where("group_id=?", bg.Id)) // if err != nil { // return err // } @@ -146,7 +155,7 @@ func (bg *BusiGroup) Del() error { // return errors.New("Some collect rules still in the BusiGroup") // } - has, err = Exists(DB().Model(&AlertRule{}).Where("group_id=?", bg.Id)) + has, err = Exists(DB(ctx).Model(&AlertRule{}).Where("group_id=?", bg.Id)) if err != nil { return err } @@ -155,7 +164,7 @@ func (bg *BusiGroup) Del() error { return errors.New("Some alert rules still in the BusiGroup") } - return DB().Transaction(func(tx *gorm.DB) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("busi_group_id=?", bg.Id).Delete(&BusiGroupMember{}).Error; err != nil { return err } @@ -175,50 +184,50 @@ func (bg *BusiGroup) Del() error { }) } -func (bg *BusiGroup) AddMembers(members []BusiGroupMember, username string) error { +func (bg *BusiGroup) AddMembers(ctx *ctx.Context, members []BusiGroupMember, username string) error { for i := 0; i < len(members); i++ { - err := BusiGroupMemberAdd(members[i]) + err := BusiGroupMemberAdd(ctx, members[i]) if err != nil { return err } } - return DB().Model(bg).Updates(map[string]interface{}{ + return DB(ctx).Model(bg).Updates(map[string]interface{}{ "update_at": time.Now().Unix(), "update_by": username, }).Error } -func (bg *BusiGroup) DelMembers(members []BusiGroupMember, username string) error { +func (bg *BusiGroup) DelMembers(ctx *ctx.Context, members []BusiGroupMember, username string) error { for i := 0; i < len(members); i++ { - num, err := BusiGroupMemberCount("busi_group_id = ? and user_group_id <> ?", members[i].BusiGroupId, members[i].UserGroupId) + num, err := BusiGroupMemberCount(ctx, "busi_group_id = ? and user_group_id <> ?", members[i].BusiGroupId, members[i].UserGroupId) if err != nil { return err } if num == 0 { // 说明这是最后一个user-group,如果再删了,就没人可以管理这个busi-group了 - return fmt.Errorf("The business group must retain at least one team") + return fmt.Errorf("the business group must retain at least one team") } - err = BusiGroupMemberDel("busi_group_id = ? and user_group_id = ?", members[i].BusiGroupId, members[i].UserGroupId) + err = BusiGroupMemberDel(ctx, "busi_group_id = ? and user_group_id = ?", members[i].BusiGroupId, members[i].UserGroupId) if err != nil { return err } } - return DB().Model(bg).Updates(map[string]interface{}{ + return DB(ctx).Model(bg).Updates(map[string]interface{}{ "update_at": time.Now().Unix(), "update_by": username, }).Error } -func (bg *BusiGroup) Update(name string, labelEnable int, labelValue string, updateBy string) error { +func (bg *BusiGroup) Update(ctx *ctx.Context, name string, labelEnable int, labelValue string, updateBy string) error { if bg.Name == name && bg.LabelEnable == labelEnable && bg.LabelValue == labelValue { return nil } - exists, err := BusiGroupExists("name = ? and id <> ?", name, bg.Id) + exists, err := BusiGroupExists(ctx, "name = ? and id <> ?", name, bg.Id) if err != nil { return errors.WithMessage(err, "failed to count BusiGroup") } @@ -228,7 +237,7 @@ func (bg *BusiGroup) Update(name string, labelEnable int, labelValue string, upd } if labelEnable == 1 { - exists, err = BusiGroupExists("label_enable = 1 and label_value = ? and id <> ?", labelValue, bg.Id) + exists, err = BusiGroupExists(ctx, "label_enable = 1 and label_value = ? and id <> ?", labelValue, bg.Id) if err != nil { return errors.WithMessage(err, "failed to count BusiGroup") } @@ -240,7 +249,7 @@ func (bg *BusiGroup) Update(name string, labelEnable int, labelValue string, upd labelValue = "" } - return DB().Model(bg).Updates(map[string]interface{}{ + return DB(ctx).Model(bg).Updates(map[string]interface{}{ "name": name, "label_enable": labelEnable, "label_value": labelValue, @@ -249,8 +258,8 @@ func (bg *BusiGroup) Update(name string, labelEnable int, labelValue string, upd }).Error } -func BusiGroupAdd(name string, labelEnable int, labelValue string, members []BusiGroupMember, creator string) error { - exists, err := BusiGroupExists("name=?", name) +func BusiGroupAdd(ctx *ctx.Context, name string, labelEnable int, labelValue string, members []BusiGroupMember, creator string) error { + exists, err := BusiGroupExists(ctx, "name=?", name) if err != nil { return errors.WithMessage(err, "failed to count BusiGroup") } @@ -260,7 +269,7 @@ func BusiGroupAdd(name string, labelEnable int, labelValue string, members []Bus } if labelEnable == 1 { - exists, err = BusiGroupExists("label_enable = 1 and label_value = ?", labelValue) + exists, err = BusiGroupExists(ctx, "label_enable = 1 and label_value = ?", labelValue) if err != nil { return errors.WithMessage(err, "failed to count BusiGroup") } @@ -274,7 +283,7 @@ func BusiGroupAdd(name string, labelEnable int, labelValue string, members []Bus count := len(members) for i := 0; i < count; i++ { - ug, err := UserGroupGet("id=?", members[i].UserGroupId) + ug, err := UserGroupGet(ctx, "id=?", members[i].UserGroupId) if err != nil { return errors.WithMessage(err, "failed to get UserGroup") } @@ -295,7 +304,7 @@ func BusiGroupAdd(name string, labelEnable int, labelValue string, members []Bus UpdateBy: creator, } - return DB().Transaction(func(tx *gorm.DB) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Create(obj).Error; err != nil { return err } @@ -314,8 +323,8 @@ func BusiGroupAdd(name string, labelEnable int, labelValue string, members []Bus }) } -func BusiGroupStatistics() (*Statistics, error) { - session := DB().Model(&BusiGroup{}).Select("count(*) as total", "max(update_at) as last_updated") +func BusiGroupStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&BusiGroup{}).Select("count(*) as total", "max(update_at) as last_updated") var stats []*Statistics err := session.Find(&stats).Error diff --git a/models/busi_group_member.go b/models/busi_group_member.go new file mode 100644 index 0000000000000000000000000000000000000000..9cb904e6fc44a80bf06fd666569ce4888de29e31 --- /dev/null +++ b/models/busi_group_member.go @@ -0,0 +1,94 @@ +package models + +import "github.com/ccfos/nightingale/v6/pkg/ctx" + +type BusiGroupMember struct { + BusiGroupId int64 `json:"busi_group_id"` + UserGroupId int64 `json:"user_group_id"` + PermFlag string `json:"perm_flag"` +} + +func (BusiGroupMember) TableName() string { + return "busi_group_member" +} + +func BusiGroupIds(ctx *ctx.Context, userGroupIds []int64, permFlag ...string) ([]int64, error) { + if len(userGroupIds) == 0 { + return []int64{}, nil + } + + session := DB(ctx).Model(&BusiGroupMember{}).Where("user_group_id in ?", userGroupIds) + if len(permFlag) > 0 { + session = session.Where("perm_flag=?", permFlag[0]) + } + + var ids []int64 + err := session.Pluck("busi_group_id", &ids).Error + return ids, err +} + +func UserGroupIdsOfBusiGroup(ctx *ctx.Context, busiGroupId int64, permFlag ...string) ([]int64, error) { + session := DB(ctx).Model(&BusiGroupMember{}).Where("busi_group_id = ?", busiGroupId) + if len(permFlag) > 0 { + session = session.Where("perm_flag=?", permFlag[0]) + } + + var ids []int64 + err := session.Pluck("user_group_id", &ids).Error + return ids, err +} + +func BusiGroupMemberCount(ctx *ctx.Context, where string, args ...interface{}) (int64, error) { + return Count(DB(ctx).Model(&BusiGroupMember{}).Where(where, args...)) +} + +func BusiGroupMemberAdd(ctx *ctx.Context, member BusiGroupMember) error { + obj, err := BusiGroupMemberGet(ctx, "busi_group_id = ? and user_group_id = ?", member.BusiGroupId, member.UserGroupId) + if err != nil { + return err + } + + if obj == nil { + // insert + return Insert(ctx, &BusiGroupMember{ + BusiGroupId: member.BusiGroupId, + UserGroupId: member.UserGroupId, + PermFlag: member.PermFlag, + }) + } else { + // update + if obj.PermFlag == member.PermFlag { + return nil + } + + return DB(ctx).Model(&BusiGroupMember{}).Where("busi_group_id = ? and user_group_id = ?", member.BusiGroupId, member.UserGroupId).Update("perm_flag", member.PermFlag).Error + } +} + +func BusiGroupMemberGet(ctx *ctx.Context, where string, args ...interface{}) (*BusiGroupMember, error) { + var lst []*BusiGroupMember + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + return lst[0], nil +} + +func BusiGroupMemberDel(ctx *ctx.Context, where string, args ...interface{}) error { + return DB(ctx).Where(where, args...).Delete(&BusiGroupMember{}).Error +} + +func BusiGroupMemberGets(ctx *ctx.Context, where string, args ...interface{}) ([]BusiGroupMember, error) { + var lst []BusiGroupMember + err := DB(ctx).Where(where, args...).Order("perm_flag").Find(&lst).Error + return lst, err +} + +func BusiGroupMemberGetsByBusiGroupId(ctx *ctx.Context, busiGroupId int64) ([]BusiGroupMember, error) { + return BusiGroupMemberGets(ctx, "busi_group_id=?", busiGroupId) +} diff --git a/models/chart.go b/models/chart.go new file mode 100644 index 0000000000000000000000000000000000000000..55d6674750b0bb1542e8554b69a398eb1766529a --- /dev/null +++ b/models/chart.go @@ -0,0 +1,32 @@ +package models + +import "github.com/ccfos/nightingale/v6/pkg/ctx" + +type Chart struct { + Id int64 `json:"id" gorm:"primaryKey"` + GroupId int64 `json:"group_id"` + Configs string `json:"configs"` + Weight int `json:"weight"` +} + +func (c *Chart) TableName() string { + return "chart" +} + +func ChartsOf(ctx *ctx.Context, chartGroupId int64) ([]Chart, error) { + var objs []Chart + err := DB(ctx).Where("group_id = ?", chartGroupId).Order("weight").Find(&objs).Error + return objs, err +} + +func (c *Chart) Add(ctx *ctx.Context) error { + return Insert(ctx, c) +} + +func (c *Chart) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { + return DB(ctx).Model(c).Select(selectField, selectFields...).Updates(c).Error +} + +func (c *Chart) Del(ctx *ctx.Context) error { + return DB(ctx).Where("id=?", c.Id).Delete(&Chart{}).Error +} diff --git a/src/models/chart_group.go b/models/chart_group.go similarity index 56% rename from src/models/chart_group.go rename to models/chart_group.go index a8b43e02145f5a40b74d2e52b7f6b2327ab06da3..dca04e12dee9fe6a3bbf35d2d45ef7414b2b3992 100644 --- a/src/models/chart_group.go +++ b/models/chart_group.go @@ -1,6 +1,7 @@ package models import ( + "github.com/ccfos/nightingale/v6/pkg/ctx" "github.com/pkg/errors" "github.com/toolkits/pkg/str" "gorm.io/gorm" @@ -29,24 +30,24 @@ func (cg *ChartGroup) Verify() error { return nil } -func (cg *ChartGroup) Add() error { +func (cg *ChartGroup) Add(ctx *ctx.Context) error { if err := cg.Verify(); err != nil { return err } - return Insert(cg) + return Insert(ctx, cg) } -func (cg *ChartGroup) Update(selectField interface{}, selectFields ...interface{}) error { +func (cg *ChartGroup) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { if err := cg.Verify(); err != nil { return err } - return DB().Model(cg).Select(selectField, selectFields...).Updates(cg).Error + return DB(ctx).Model(cg).Select(selectField, selectFields...).Updates(cg).Error } -func (cg *ChartGroup) Del() error { - return DB().Transaction(func(tx *gorm.DB) error { +func (cg *ChartGroup) Del(ctx *ctx.Context) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("group_id=?", cg.Id).Delete(&Chart{}).Error; err != nil { return err } @@ -59,22 +60,22 @@ func (cg *ChartGroup) Del() error { }) } -func NewDefaultChartGroup(dashId int64) error { - return Insert(&ChartGroup{ +func NewDefaultChartGroup(ctx *ctx.Context, dashId int64) error { + return Insert(ctx, &ChartGroup{ DashboardId: dashId, Name: "Default chart group", Weight: 0, }) } -func ChartGroupIdsOf(dashId int64) ([]int64, error) { +func ChartGroupIdsOf(ctx *ctx.Context, dashId int64) ([]int64, error) { var ids []int64 - err := DB().Model(&ChartGroup{}).Where("dashboard_id = ?", dashId).Pluck("id", &ids).Error + err := DB(ctx).Model(&ChartGroup{}).Where("dashboard_id = ?", dashId).Pluck("id", &ids).Error return ids, err } -func ChartGroupsOf(dashId int64) ([]ChartGroup, error) { +func ChartGroupsOf(ctx *ctx.Context, dashId int64) ([]ChartGroup, error) { var objs []ChartGroup - err := DB().Where("dashboard_id = ?", dashId).Order("weight").Find(&objs).Error + err := DB(ctx).Where("dashboard_id = ?", dashId).Order("weight").Find(&objs).Error return objs, err } diff --git a/models/chart_share.go b/models/chart_share.go new file mode 100644 index 0000000000000000000000000000000000000000..a3d50916592e300ca4935827ef4e8c749b34e2e0 --- /dev/null +++ b/models/chart_share.go @@ -0,0 +1,30 @@ +package models + +import "github.com/ccfos/nightingale/v6/pkg/ctx" + +type ChartShare struct { + Id int64 `json:"id" gorm:"primaryKey"` + Cluster string `json:"cluster"` + DatasourceId int64 `json:"datasource_id"` + Configs string `json:"configs"` + CreateBy string `json:"create_by"` + CreateAt int64 `json:"create_at"` +} + +func (cs *ChartShare) TableName() string { + return "chart_share" +} + +func (cs *ChartShare) Add(ctx *ctx.Context) error { + return Insert(ctx, cs) +} + +func ChartShareGetsByIds(ctx *ctx.Context, ids []int64) ([]ChartShare, error) { + var lst []ChartShare + if len(ids) == 0 { + return lst, nil + } + + err := DB(ctx).Where("id in ?", ids).Order("id").Find(&lst).Error + return lst, err +} diff --git a/models/common.go b/models/common.go new file mode 100644 index 0000000000000000000000000000000000000000..7ce11f4e5546b6fd1930cc26469e08dada365c02 --- /dev/null +++ b/models/common.go @@ -0,0 +1,83 @@ +package models + +import ( + "github.com/ccfos/nightingale/v6/pkg/ctx" + + "github.com/toolkits/pkg/str" + "gorm.io/gorm" +) + +const AdminRole = "Admin" + +// if rule's cluster field contains `ClusterAll`, means it take effect in all clusters +const DatasourceIdAll = 0 + +func DB(ctx *ctx.Context) *gorm.DB { + return ctx.DB +} + +func Count(tx *gorm.DB) (int64, error) { + var cnt int64 + err := tx.Count(&cnt).Error + return cnt, err +} + +func Exists(tx *gorm.DB) (bool, error) { + num, err := Count(tx) + return num > 0, err +} + +func Insert(ctx *ctx.Context, obj interface{}) error { + return DB(ctx).Create(obj).Error +} + +// CryptoPass crypto password use salt +func CryptoPass(ctx *ctx.Context, raw string) (string, error) { + salt, err := ConfigsGet(ctx, "salt") + if err != nil { + return "", err + } + + return str.MD5(salt + "<-*Uk30^96eY*->" + raw), nil +} + +type Statistics struct { + Total int64 `gorm:"total"` + LastUpdated int64 `gorm:"last_updated"` +} + +func MatchDatasource(ids []int64, id int64) bool { + if id == DatasourceIdAll { + return true + } + + for _, i := range ids { + if i == id { + return true + } + } + return false +} + +func IsAllDatasource(datasourceIds []int64) bool { + for _, id := range datasourceIds { + if id == 0 { + return true + } + } + return false +} + +type LabelAndKey struct { + Label string `json:"label"` + Key string `json:"key"` +} + +func LabelAndKeyHasKey(keys []LabelAndKey, key string) bool { + for i := 0; i < len(keys); i++ { + if keys[i].Key == key { + return true + } + } + return false +} diff --git a/src/models/configs.go b/models/configs.go similarity index 58% rename from src/models/configs.go rename to models/configs.go index aa5f3d7617bbd7b138a681b88b91c27498d86a05..ba765bf636c74ad3f70d89e9c81bd980df138e09 100644 --- a/src/models/configs.go +++ b/models/configs.go @@ -6,6 +6,8 @@ import ( "os" "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" "github.com/toolkits/pkg/runner" "github.com/toolkits/pkg/str" @@ -22,8 +24,8 @@ func (Configs) TableName() string { } // InitSalt generate random salt -func InitSalt() { - val, err := ConfigsGet("salt") +func InitSalt(ctx *ctx.Context) { + val, err := ConfigsGet(ctx, "salt") if err != nil { log.Fatalln("cannot query salt", err) } @@ -34,15 +36,15 @@ func InitSalt() { content := fmt.Sprintf("%s%d%d%s", runner.Hostname, os.Getpid(), time.Now().UnixNano(), str.RandLetters(6)) salt := str.MD5(content) - err = ConfigsSet("salt", salt) + err = ConfigsSet(ctx, "salt", salt) if err != nil { log.Fatalln("init salt in mysql", err) } } -func ConfigsGet(ckey string) (string, error) { +func ConfigsGet(ctx *ctx.Context, ckey string) (string, error) { var lst []string - err := DB().Model(&Configs{}).Where("ckey=?", ckey).Pluck("cval", &lst).Error + err := DB(ctx).Model(&Configs{}).Where("ckey=?", ckey).Pluck("cval", &lst).Error if err != nil { return "", errors.WithMessage(err, "failed to query configs") } @@ -54,29 +56,29 @@ func ConfigsGet(ckey string) (string, error) { return "", nil } -func ConfigsSet(ckey, cval string) error { - num, err := Count(DB().Model(&Configs{}).Where("ckey=?", ckey)) +func ConfigsSet(ctx *ctx.Context, ckey, cval string) error { + num, err := Count(DB(ctx).Model(&Configs{}).Where("ckey=?", ckey)) if err != nil { return errors.WithMessage(err, "failed to count configs") } if num == 0 { // insert - err = DB().Create(&Configs{ + err = DB(ctx).Create(&Configs{ Ckey: ckey, Cval: cval, }).Error } else { // update - err = DB().Model(&Configs{}).Where("ckey=?", ckey).Update("cval", cval).Error + err = DB(ctx).Model(&Configs{}).Where("ckey=?", ckey).Update("cval", cval).Error } return err } -func ConfigGet(id int64) (*Configs, error) { +func ConfigGet(ctx *ctx.Context, id int64) (*Configs, error) { var objs []*Configs - err := DB().Where("id=?", id).Find(&objs).Error + err := DB(ctx).Where("id=?", id).Find(&objs).Error if len(objs) == 0 { return nil, nil @@ -84,9 +86,9 @@ func ConfigGet(id int64) (*Configs, error) { return objs[0], err } -func ConfigsGets(prefix string, limit, offset int) ([]*Configs, error) { +func ConfigsGets(ctx *ctx.Context, prefix string, limit, offset int) ([]*Configs, error) { var objs []*Configs - session := DB() + session := DB(ctx) if prefix != "" { session = session.Where("ckey like ?", prefix+"%") } @@ -95,8 +97,8 @@ func ConfigsGets(prefix string, limit, offset int) ([]*Configs, error) { return objs, err } -func (c *Configs) Add() error { - num, err := Count(DB().Model(&Configs{}).Where("ckey=?", c.Ckey)) +func (c *Configs) Add(ctx *ctx.Context) error { + num, err := Count(DB(ctx).Model(&Configs{}).Where("ckey=?", c.Ckey)) if err != nil { return errors.WithMessage(err, "failed to count configs") } @@ -105,15 +107,15 @@ func (c *Configs) Add() error { } // insert - err = DB().Create(&Configs{ + err = DB(ctx).Create(&Configs{ Ckey: c.Ckey, Cval: c.Cval, }).Error return err } -func (c *Configs) Update() error { - num, err := Count(DB().Model(&Configs{}).Where("id<>? and ckey=?", c.Id, c.Ckey)) +func (c *Configs) Update(ctx *ctx.Context) error { + num, err := Count(DB(ctx).Model(&Configs{}).Where("id<>? and ckey=?", c.Id, c.Ckey)) if err != nil { return errors.WithMessage(err, "failed to count configs") } @@ -121,17 +123,17 @@ func (c *Configs) Update() error { return errors.WithMessage(err, "key is exists") } - err = DB().Model(&Configs{}).Where("id=?", c.Id).Updates(c).Error + err = DB(ctx).Model(&Configs{}).Where("id=?", c.Id).Updates(c).Error return err } -func ConfigsDel(ids []int64) error { - return DB().Where("id in ?", ids).Delete(&Configs{}).Error +func ConfigsDel(ctx *ctx.Context, ids []int64) error { + return DB(ctx).Where("id in ?", ids).Delete(&Configs{}).Error } -func ConfigsGetsByKey(ckeys []string) (map[string]string, error) { +func ConfigsGetsByKey(ctx *ctx.Context, ckeys []string) (map[string]string, error) { var objs []Configs - err := DB().Where("ckey in ?", ckeys).Find(&objs).Error + err := DB(ctx).Where("ckey in ?", ckeys).Find(&objs).Error if err != nil { return nil, errors.WithMessage(err, "failed to gets configs") } diff --git a/src/models/dashboard.go b/models/dashboard.go similarity index 64% rename from src/models/dashboard.go rename to models/dashboard.go index 6d083b9baac8b1076782ef0c789a2cfcb4298109..e0d952adeacba082d7ef8c6e323f2c4651e6069c 100644 --- a/src/models/dashboard.go +++ b/models/dashboard.go @@ -4,6 +4,7 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" "github.com/pkg/errors" "github.com/toolkits/pkg/str" "gorm.io/gorm" @@ -38,12 +39,12 @@ func (d *Dashboard) Verify() error { return nil } -func (d *Dashboard) Add() error { +func (d *Dashboard) Add(ctx *ctx.Context) error { if err := d.Verify(); err != nil { return err } - exists, err := DashboardExists("group_id=? and name=?", d.GroupId, d.Name) + exists, err := DashboardExists(ctx, "group_id=? and name=?", d.GroupId, d.Name) if err != nil { return errors.WithMessage(err, "failed to count dashboard") } @@ -56,25 +57,25 @@ func (d *Dashboard) Add() error { d.CreateAt = now d.UpdateAt = now - return Insert(d) + return Insert(ctx, d) } -func (d *Dashboard) Update(selectField interface{}, selectFields ...interface{}) error { +func (d *Dashboard) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { if err := d.Verify(); err != nil { return err } - return DB().Model(d).Select(selectField, selectFields...).Updates(d).Error + return DB(ctx).Model(d).Select(selectField, selectFields...).Updates(d).Error } -func (d *Dashboard) Del() error { - cgids, err := ChartGroupIdsOf(d.Id) +func (d *Dashboard) Del(ctx *ctx.Context) error { + cgids, err := ChartGroupIdsOf(ctx, d.Id) if err != nil { return err } if len(cgids) == 0 { - return DB().Transaction(func(tx *gorm.DB) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("id=?", d.Id).Delete(&Dashboard{}).Error; err != nil { return err } @@ -82,7 +83,7 @@ func (d *Dashboard) Del() error { }) } - return DB().Transaction(func(tx *gorm.DB) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("group_id in ?", cgids).Delete(&Chart{}).Error; err != nil { return err } @@ -99,9 +100,9 @@ func (d *Dashboard) Del() error { }) } -func DashboardGet(where string, args ...interface{}) (*Dashboard, error) { +func DashboardGet(ctx *ctx.Context, where string, args ...interface{}) (*Dashboard, error) { var lst []*Dashboard - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } @@ -115,17 +116,17 @@ func DashboardGet(where string, args ...interface{}) (*Dashboard, error) { return lst[0], nil } -func DashboardCount(where string, args ...interface{}) (num int64, err error) { - return Count(DB().Model(&Dashboard{}).Where(where, args...)) +func DashboardCount(ctx *ctx.Context, where string, args ...interface{}) (num int64, err error) { + return Count(DB(ctx).Model(&Dashboard{}).Where(where, args...)) } -func DashboardExists(where string, args ...interface{}) (bool, error) { - num, err := DashboardCount(where, args...) +func DashboardExists(ctx *ctx.Context, where string, args ...interface{}) (bool, error) { + num, err := DashboardCount(ctx, where, args...) return num > 0, err } -func DashboardGets(groupId int64, query string) ([]Dashboard, error) { - session := DB().Where("group_id=?", groupId).Order("name") +func DashboardGets(ctx *ctx.Context, groupId int64, query string) ([]Dashboard, error) { + session := DB(ctx).Where("group_id=?", groupId).Order("name") arr := strings.Fields(query) if len(arr) > 0 { @@ -151,18 +152,18 @@ func DashboardGets(groupId int64, query string) ([]Dashboard, error) { return objs, err } -func DashboardGetsByIds(ids []int64) ([]Dashboard, error) { +func DashboardGetsByIds(ctx *ctx.Context, ids []int64) ([]Dashboard, error) { if len(ids) == 0 { return []Dashboard{}, nil } var lst []Dashboard - err := DB().Where("id in ?", ids).Order("name").Find(&lst).Error + err := DB(ctx).Where("id in ?", ids).Order("name").Find(&lst).Error return lst, err } -func DashboardGetAll() ([]Dashboard, error) { +func DashboardGetAll(ctx *ctx.Context) ([]Dashboard, error) { var lst []Dashboard - err := DB().Find(&lst).Error + err := DB(ctx).Find(&lst).Error return lst, err } diff --git a/models/datasource.go b/models/datasource.go new file mode 100644 index 0000000000000000000000000000000000000000..e4097f5cf64e69377082285b7813dff6d5f8843c --- /dev/null +++ b/models/datasource.go @@ -0,0 +1,282 @@ +package models + +import ( + "encoding/json" + "net/http" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/str" +) + +type Datasource struct { + Id int64 `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + PluginId int64 `json:"plugin_id"` + PluginType string `json:"plugin_type"` // prometheus + PluginTypeName string `json:"plugin_type_name"` // Prometheus Like + Category string `json:"category"` // timeseries + ClusterName string `json:"cluster_name"` + Settings string `json:"-" gorm:"settings"` + SettingsJson interface{} `json:"settings" gorm:"-"` + Status string `json:"status"` + HTTP string `json:"-" gorm:"http"` + HTTPJson HTTP `json:"http" gorm:"-"` + Auth string `json:"-" gorm:"auth"` + AuthJson Auth `json:"auth" gorm:"-"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` + CreatedBy string `json:"created_by"` + UpdatedBy string `json:"updated_by"` + Transport *http.Transport `json:"-" gorm:"-"` +} + +type Auth struct { + BasicAuth bool `json:"basic_auth"` + BasicAuthUser string `json:"basic_auth_user"` + BasicAuthPassword string `json:"basic_auth_password"` +} + +type HTTP struct { + Timeout int64 `json:"timeout"` + DialTimeout int64 `json:"dial_timeout"` + UseTLS TLS `json:"tls"` + MaxIdleConnsPerHost int `json:"max_idle_conns_per_host"` + Url string `json:"url"` + Headers map[string]string `json:"headers"` +} + +type TLS struct { + SkipTlsVerify bool `json:"skip_tls_verify"` +} + +func (ds *Datasource) TableName() string { + return "datasource" +} + +func (ds *Datasource) Verify() error { + if str.Dangerous(ds.Name) { + return errors.New("Name has invalid characters") + } + + err := ds.FE2DB() + return err +} + +func (ds *Datasource) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { + if err := ds.Verify(); err != nil { + return err + } + + ds.UpdatedAt = time.Now().Unix() + return DB(ctx).Model(ds).Select(selectField, selectFields...).Updates(ds).Error +} + +func (ds *Datasource) Add(ctx *ctx.Context) error { + if err := ds.Verify(); err != nil { + return err + } + + now := time.Now().Unix() + ds.CreatedAt = now + ds.UpdatedAt = now + return Insert(ctx, ds) +} + +func DatasourceDel(ctx *ctx.Context, ids []int64) error { + if len(ids) == 0 { + return nil + } + return DB(ctx).Where("id in ?", ids).Delete(new(Datasource)).Error +} + +func DatasourceGet(ctx *ctx.Context, id int64) (*Datasource, error) { + var ds *Datasource + err := DB(ctx).Where("id = ?", id).First(&ds).Error + if err != nil { + return nil, err + } + return ds, ds.DB2FE() +} + +func (ds *Datasource) Get(ctx *ctx.Context) error { + err := DB(ctx).Where("id = ?", ds.Id).First(ds).Error + if err != nil { + return err + } + return ds.DB2FE() +} + +func GetDatasources(ctx *ctx.Context) ([]Datasource, error) { + var dss []Datasource + err := DB(ctx).Find(&dss).Error + + for i := 0; i < len(dss); i++ { + dss[i].DB2FE() + } + + return dss, err +} + +func GetDatasourceIdsByClusterName(ctx *ctx.Context, clusterName string) ([]int64, error) { + var dss []Datasource + var ids []int64 + err := DB(ctx).Where("cluster_name = ?", clusterName).Find(&dss).Error + if err != nil { + return ids, err + } + + for i := 0; i < len(dss); i++ { + ids = append(ids, dss[i].Id) + } + return ids, err +} + +func GetDatasourcesCountBy(ctx *ctx.Context, typ, cate, name string) (int64, error) { + session := DB(ctx).Model(&Datasource{}) + + if name != "" { + arr := strings.Fields(name) + for i := 0; i < len(arr); i++ { + qarg := "%" + arr[i] + "%" + session = session.Where("name = ?", qarg) + } + } + + if typ != "" { + session = session.Where("plugin_type = ?", typ) + } + + if cate != "" { + session = session.Where("category = ?", cate) + } + + return Count(session) +} + +func GetDatasourcesGetsBy(ctx *ctx.Context, typ, cate, name string, limit, offset int) ([]*Datasource, error) { + session := DB(ctx) + + if name != "" { + arr := strings.Fields(name) + for i := 0; i < len(arr); i++ { + qarg := "%" + arr[i] + "%" + session = session.Where("name = ?", qarg) + } + } + + if typ != "" { + session = session.Where("plugin_type = ?", typ) + } + + if cate != "" { + session = session.Where("category = ?", cate) + } + + var lst []*Datasource + err := session.Order("id desc").Limit(limit).Offset(offset).Find(&lst).Error + if err == nil { + for i := 0; i < len(lst); i++ { + lst[i].DB2FE() + } + } + return lst, err +} + +func (ds *Datasource) FE2DB() error { + if ds.SettingsJson != nil { + b, err := json.Marshal(ds.SettingsJson) + if err != nil { + return err + } + ds.Settings = string(b) + } + + b, err := json.Marshal(ds.HTTPJson) + if err != nil { + return err + } + ds.HTTP = string(b) + + b, err = json.Marshal(ds.AuthJson) + if err != nil { + return err + } + ds.Auth = string(b) + + return nil +} + +func (ds *Datasource) DB2FE() error { + if ds.Settings != "" { + err := json.Unmarshal([]byte(ds.Settings), &ds.SettingsJson) + if err != nil { + return err + } + } + + if ds.HTTP != "" { + err := json.Unmarshal([]byte(ds.HTTP), &ds.HTTPJson) + if err != nil { + return err + } + } + + if ds.HTTPJson.Timeout == 0 { + ds.HTTPJson.Timeout = 10000 + } + + if ds.HTTPJson.DialTimeout == 0 { + ds.HTTPJson.DialTimeout = 10000 + } + + if ds.HTTPJson.MaxIdleConnsPerHost == 0 { + ds.HTTPJson.MaxIdleConnsPerHost = 100 + } + + if ds.Auth != "" { + err := json.Unmarshal([]byte(ds.Auth), &ds.AuthJson) + if err != nil { + return err + } + } + + return nil +} + +func DatasourceGetMap(ctx *ctx.Context) (map[int64]*Datasource, error) { + var lst []*Datasource + err := DB(ctx).Find(&lst).Error + if err != nil { + return nil, err + } + + ret := make(map[int64]*Datasource) + for i := 0; i < len(lst); i++ { + err := lst[i].DB2FE() + if err != nil { + logger.Warningf("get ds:%+v err:%v", lst[i], err) + continue + } + + ret[lst[i].Id] = lst[i] + } + + return ret, nil +} + +func DatasourceStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&Datasource{}).Select("count(*) as total", "max(updated_at) as last_updated") + + var stats []*Statistics + err := session.Find(&stats).Error + if err != nil { + return nil, err + } + + return stats[0], nil +} diff --git a/src/models/metric_view.go b/models/metric_view.go similarity index 63% rename from src/models/metric_view.go rename to models/metric_view.go index 069f8c17a5b4ceb18154690b8c3985233297284c..4c5868549c3378e11ea12fb194b0ee55f22610d4 100644 --- a/src/models/metric_view.go +++ b/models/metric_view.go @@ -5,6 +5,8 @@ import ( "sort" "strings" "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" ) // MetricView 在告警聚合视图查看的时候,要存储一些聚合规则 @@ -36,7 +38,7 @@ func (v *MetricView) Verify() error { return nil } -func (v *MetricView) Add() error { +func (v *MetricView) Add(ctx *ctx.Context) error { if err := v.Verify(); err != nil { return err } @@ -44,10 +46,10 @@ func (v *MetricView) Add() error { now := time.Now().Unix() v.CreateAt = now v.UpdateAt = now - return Insert(v) + return Insert(ctx, v) } -func (v *MetricView) Update(name, configs string, cate int, createBy int64) error { +func (v *MetricView) Update(ctx *ctx.Context, name, configs string, cate int, createBy int64) error { if err := v.Verify(); err != nil { return err } @@ -61,25 +63,25 @@ func (v *MetricView) Update(name, configs string, cate int, createBy int64) erro v.CreateBy = createBy } - return DB().Model(v).Select("name", "configs", "cate", "update_at", "create_by").Updates(v).Error + return DB(ctx).Model(v).Select("name", "configs", "cate", "update_at", "create_by").Updates(v).Error } // MetricViewDel: userid for safe delete -func MetricViewDel(ids []int64, createBy ...interface{}) error { +func MetricViewDel(ctx *ctx.Context, ids []int64, createBy ...interface{}) error { if len(ids) == 0 { return nil } if len(createBy) > 0 { - return DB().Where("id in ? and create_by = ?", ids, createBy[0]).Delete(new(MetricView)).Error + return DB(ctx).Where("id in ? and create_by = ?", ids, createBy[0]).Delete(new(MetricView)).Error } - return DB().Where("id in ?", ids).Delete(new(MetricView)).Error + return DB(ctx).Where("id in ?", ids).Delete(new(MetricView)).Error } -func MetricViewGets(createBy interface{}) ([]MetricView, error) { +func MetricViewGets(ctx *ctx.Context, createBy interface{}) ([]MetricView, error) { var lst []MetricView - err := DB().Where("create_by = ? or cate = 0", createBy).Find(&lst).Error + err := DB(ctx).Where("create_by = ? or cate = 0", createBy).Find(&lst).Error if err == nil && len(lst) > 1 { sort.Slice(lst, func(i, j int) bool { if lst[i].Cate < lst[j].Cate { @@ -96,9 +98,9 @@ func MetricViewGets(createBy interface{}) ([]MetricView, error) { return lst, err } -func MetricViewGet(where string, args ...interface{}) (*MetricView, error) { +func MetricViewGet(ctx *ctx.Context, where string, args ...interface{}) (*MetricView, error) { var lst []*MetricView - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } diff --git a/models/notify_config.go b/models/notify_config.go new file mode 100644 index 0000000000000000000000000000000000000000..7147b1268440f14e831c1f73c00751d03e2cb9e8 --- /dev/null +++ b/models/notify_config.go @@ -0,0 +1,38 @@ +package models + +const WEBHOOKKEY = "webhook" +const NOTIFYSCRIPT = "notify_script" +const NOTIFYCHANNEL = "notify_channel" +const NOTIFYCONTACT = "notify_contact" + +type Webhook struct { + Enable bool `json:"enable"` + Url string `json:"url"` + BasicAuthUser string `json:"basic_auth_user"` + BasicAuthPass string `json:"basic_auth_pass"` + Timeout int `json:"timeout"` + HeaderMap map[string]string `json:"headers"` + Headers []string `json:"headers_str"` + SkipVerify bool `json:"skip_verify"` +} + +type NotifyScript struct { + Enable bool `json:"enable"` + Type int `json:"type"` // 0 script 1 path + Content string `json:"content"` + Timeout int `json:"timeout"` +} + +type NotifyChannel struct { + Name string `json:"name"` + Ident string `json:"ident"` + Hide bool `json:"hide"` + BuiltIn bool `json:"built_in"` +} + +type NotifyContact struct { + Name string `json:"name"` + Ident string `json:"ident"` + Hide bool `json:"hide"` + BuiltIn bool `json:"built_in"` +} diff --git a/models/notify_tpl.go b/models/notify_tpl.go new file mode 100644 index 0000000000000000000000000000000000000000..b8c175fb0257b3bd5056759c7aa93ba8e90f4c66 --- /dev/null +++ b/models/notify_tpl.go @@ -0,0 +1,181 @@ +package models + +import ( + "encoding/json" + "fmt" + "html/template" + "path" + "strings" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/tplx" + + "github.com/pkg/errors" + "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/logger" +) + +type NotifyTpl struct { + Id int64 `json:"id"` + Name string `json:"name"` + Channel string `json:"channel"` + Content string `json:"content"` +} + +func (n *NotifyTpl) TableName() string { + return "notify_tpl" +} + +func (n *NotifyTpl) Create(c *ctx.Context) error { + return Insert(c, n) +} + +func (n *NotifyTpl) UpdateContent(c *ctx.Context) error { + return DB(c).Model(n).Update("content", n.Content).Error +} + +func (n *NotifyTpl) Update(c *ctx.Context) error { + return DB(c).Model(n).Select("name").Updates(n).Error +} + +func (n *NotifyTpl) CreateIfNotExists(c *ctx.Context, channel string) error { + count, err := NotifyTplCountByChannel(c, channel) + if err != nil { + return errors.WithMessage(err, "failed to count notify tpls") + } + + if count != 0 { + return nil + } + + err = n.Create(c) + return err +} + +func NotifyTplCountByChannel(c *ctx.Context, channel string) (int64, error) { + var count int64 + err := DB(c).Model(&NotifyTpl{}).Where("channel=?", channel).Count(&count).Error + return count, err +} + +func NotifyTplGets(c *ctx.Context) ([]*NotifyTpl, error) { + var lst []*NotifyTpl + err := DB(c).Find(&lst).Error + return lst, err +} + +func GetChannelMap(c *ctx.Context) map[string]struct{} { + tpls, err := NotifyTplGets(c) + if err != nil { + return nil + } + + channels := make(map[string]struct{}) + for _, tpl := range tpls { + channels[tpl.Channel] = struct{}{} + } + return channels +} + +func ListTpls(c *ctx.Context) (map[string]*template.Template, error) { + notifyTpls, err := NotifyTplGets(c) + if err != nil { + return nil, errors.WithMessage(err, "failed to get notify tpls") + } + + tpls := make(map[string]*template.Template) + for _, notifyTpl := range notifyTpls { + tpl, err := template.New(notifyTpl.Channel).Funcs(tplx.TemplateFuncMap).Parse(notifyTpl.Content) + if err != nil { + return nil, fmt.Errorf("failed to parse tpl:%v %v ", notifyTpl, err) + } + + tpls[notifyTpl.Channel] = tpl + } + return tpls, nil +} + +func InitNotifyConfig(c *ctx.Context, tplDir string) { + // init notify channel + cval, err := ConfigsGet(c, NOTIFYCHANNEL) + if err != nil { + logger.Errorf("failed to get notify contact config: %v", err) + return + } + + if cval == "" { + var notifyChannels []NotifyChannel + channels := []string{Dingtalk, Wecom, Feishu, Mm, Telegram, Email} + for _, channel := range channels { + notifyChannels = append(notifyChannels, NotifyChannel{Ident: channel, Name: channel, BuiltIn: true}) + } + + data, _ := json.Marshal(notifyChannels) + err = ConfigsSet(c, NOTIFYCHANNEL, string(data)) + if err != nil { + logger.Errorf("failed to set notify contact config: %v", err) + return + } + } + + // init notify contact + cval, err = ConfigsGet(c, NOTIFYCONTACT) + if err != nil { + logger.Errorf("failed to get notify contact config: %v", err) + return + } + + if cval == "" { + var notifyContacts []NotifyContact + contacts := []string{DingtalkKey, WecomKey, FeishuKey, MmKey, TelegramKey} + for _, contact := range contacts { + notifyContacts = append(notifyContacts, NotifyContact{Ident: contact, Name: contact, BuiltIn: true}) + } + + data, _ := json.Marshal(notifyContacts) + err = ConfigsSet(c, NOTIFYCONTACT, string(data)) + if err != nil { + logger.Errorf("failed to set notify contact config: %v", err) + return + } + } + + // init notify tpl + filenames, err := file.FilesUnder(tplDir) + if err != nil { + logger.Errorf("failed to get tpl files under %s", tplDir) + return + } + + if len(filenames) == 0 { + logger.Errorf("no tpl files under %s", tplDir) + return + } + + tplMap := make(map[string]string) + for i := 0; i < len(filenames); i++ { + if strings.HasSuffix(filenames[i], ".tpl") { + name := strings.TrimSuffix(filenames[i], ".tpl") + tplpath := path.Join(tplDir, filenames[i]) + content, err := file.ToString(tplpath) + if err != nil { + logger.Errorf("failed to read tpl file: %s", filenames[i]) + continue + } + tplMap[name] = content + } + } + + for channel, content := range tplMap { + notifyTpl := NotifyTpl{ + Name: channel, + Channel: channel, + Content: content, + } + + err := notifyTpl.CreateIfNotExists(c, channel) + if err != nil { + logger.Warningf("failed to create notify tpls %v", err) + } + } +} diff --git a/models/recording_rule.go b/models/recording_rule.go new file mode 100644 index 0000000000000000000000000000000000000000..3bb9ba9e3cf511234f3e8a8fbe3d204cd33f37c4 --- /dev/null +++ b/models/recording_rule.go @@ -0,0 +1,263 @@ +package models + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" + "github.com/prometheus/common/model" + "github.com/toolkits/pkg/logger" +) + +// A RecordingRule records its vector expression into new timeseries. +type RecordingRule struct { + Id int64 `json:"id" gorm:"primaryKey"` + GroupId int64 `json:"group_id"` // busi group id + DatasourceIds string `json:"-" gorm:"datasource_ids"` // datasource ids + DatasourceIdsJson []int64 `json:"datasource_ids" gorm:"-"` // for fe + Cluster string `json:"cluster"` // take effect by cluster, seperated by space + Name string `json:"name"` // new metric name + Note string `json:"note"` // note + Disabled int `json:"disabled"` // 0: enabled, 1: disabled + PromQl string `json:"prom_ql"` // just one ql for promql + PromEvalInterval int `json:"prom_eval_interval"` // unit:s + AppendTags string `json:"-"` // split by space: service=n9e mod=api + AppendTagsJSON []string `json:"append_tags" gorm:"-"` // for fe + CreateAt int64 `json:"create_at"` + CreateBy string `json:"create_by"` + UpdateAt int64 `json:"update_at"` + UpdateBy string `json:"update_by"` +} + +func (re *RecordingRule) TableName() string { + return "recording_rule" +} + +func (re *RecordingRule) FE2DB() { + re.AppendTags = strings.Join(re.AppendTagsJSON, " ") + idsByte, _ := json.Marshal(re.DatasourceIdsJson) + re.DatasourceIds = string(idsByte) +} + +func (re *RecordingRule) DB2FE(ctx *ctx.Context) { + re.AppendTagsJSON = strings.Fields(re.AppendTags) + json.Unmarshal([]byte(re.DatasourceIds), &re.DatasourceIdsJson) +} + +func (re *RecordingRule) Verify() error { + if re.GroupId < 0 { + return fmt.Errorf("GroupId(%d) invalid", re.GroupId) + } + + if IsAllDatasource(re.DatasourceIdsJson) { + re.DatasourceIdsJson = []int64{0} + } + + if !model.MetricNameRE.MatchString(re.Name) { + return errors.New("Name has invalid chreacters") + } + + if re.Name == "" { + return errors.New("name is blank") + } + + if re.PromEvalInterval <= 0 { + re.PromEvalInterval = 60 + } + + re.AppendTags = strings.TrimSpace(re.AppendTags) + rer := strings.Fields(re.AppendTags) + for i := 0; i < len(rer); i++ { + pair := strings.Split(rer[i], "=") + if len(pair) != 2 || !model.LabelNameRE.MatchString(pair[0]) { + return fmt.Errorf("AppendTags(%s) invalid", rer[i]) + } + } + + return nil +} + +func (re *RecordingRule) Add(ctx *ctx.Context) error { + if err := re.Verify(); err != nil { + return err + } + + // 由于实际场景中会出现name重复的recording rule,所以不需要检查重复 + //exists, err := RecordingRuleExists(0, re.GroupId, re.Cluster, re.Name) + //if err != nil { + // return err + //} + // + //if exists { + // return errors.New("RecordingRule already exists") + //} + + now := time.Now().Unix() + re.CreateAt = now + re.UpdateAt = now + + return Insert(ctx, re) +} + +func (re *RecordingRule) Update(ctx *ctx.Context, ref RecordingRule) error { + // 由于实际场景中会出现name重复的recording rule,所以不需要检查重复 + //if re.Name != ref.Name { + // exists, err := RecordingRuleExists(re.Id, re.GroupId, re.Cluster, ref.Name) + // if err != nil { + // return err + // } + // if exists { + // return errors.New("RecordingRule already exists") + // } + //} + + ref.FE2DB() + ref.Id = re.Id + ref.GroupId = re.GroupId + ref.CreateAt = re.CreateAt + ref.CreateBy = re.CreateBy + ref.UpdateAt = time.Now().Unix() + err := ref.Verify() + if err != nil { + return err + } + return DB(ctx).Model(re).Select("*").Updates(ref).Error +} + +func (re *RecordingRule) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(re).Updates(fields).Error +} + +func RecordingRuleDels(ctx *ctx.Context, ids []int64, groupId int64) error { + for i := 0; i < len(ids); i++ { + ret := DB(ctx).Where("id = ? and group_id=?", ids[i], groupId).Delete(&RecordingRule{}) + if ret.Error != nil { + return ret.Error + } + } + + return nil +} + +// func RecordingRuleExists(ctx *ctx.Context, id, groupId int64, cluster, name string) (bool, error) { +// session := DB(ctx).Where("id <> ? and group_id = ? and name =? ", id, groupId, name) + +// var lst []RecordingRule +// err := session.Find(&lst).Error +// if err != nil { +// return false, err +// } +// if len(lst) == 0 { +// return false, nil +// } + +// // match cluster +// for _, r := range lst { +// if MatchCluster(r.Cluster, cluster) { +// return true, nil +// } +// } +// return false, nil +// } + +func RecordingRuleGets(ctx *ctx.Context, groupId int64) ([]RecordingRule, error) { + session := DB(ctx).Where("group_id=?", groupId).Order("name") + + var lst []RecordingRule + err := session.Find(&lst).Error + if err == nil { + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + } + + return lst, err +} + +func RecordingRuleGet(ctx *ctx.Context, where string, regs ...interface{}) (*RecordingRule, error) { + var lst []*RecordingRule + err := DB(ctx).Where(where, regs...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + lst[0].DB2FE(ctx) + + return lst[0], nil +} + +func RecordingRuleGetById(ctx *ctx.Context, id int64) (*RecordingRule, error) { + return RecordingRuleGet(ctx, "id=?", id) +} + +func RecordingRuleGetsByCluster(ctx *ctx.Context) ([]*RecordingRule, error) { + session := DB(ctx).Where("disabled = ?", 0) + + var lst []*RecordingRule + err := session.Find(&lst).Error + if err != nil { + return lst, err + } + + if len(lst) == 0 { + return lst, nil + } + + for i := 0; i < len(lst); i++ { + lst[i].DB2FE(ctx) + } + return lst, nil +} + +func RecordingRuleStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&RecordingRule{}).Select("count(*) as total", "max(update_at) as last_updated") + + var stats []*Statistics + err := session.Find(&stats).Error + if err != nil { + return nil, err + } + + return stats[0], nil +} + +func RecordingRuleUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*RecordingRule + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + var ids []int64 + if lst[i].Cluster == "$all" { + ids = append(ids, 0) + } else { + clusters := strings.Fields(lst[i].Cluster) + for j := 0; j < len(clusters); j++ { + if ds, exists := dsm[clusters[j]]; exists { + ids = append(ids, ds.Id) + } + } + } + + b, err := json.Marshal(ids) + if err != nil { + continue + } + lst[i].DatasourceIds = string(b) + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_ids": lst[i].DatasourceIds}) + if err != nil { + logger.Errorf("update alert rule:%d datasource ids failed, %v", lst[i].Id, err) + } + } + return nil +} diff --git a/models/role.go b/models/role.go new file mode 100644 index 0000000000000000000000000000000000000000..5b8260e457cea1acd5a9f75600b7efad9fa97e8d --- /dev/null +++ b/models/role.go @@ -0,0 +1,71 @@ +package models + +import ( + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/pkg/errors" +) + +type Role struct { + Id int64 `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + Note string `json:"note"` +} + +func (Role) TableName() string { + return "role" +} + +func RoleGets(ctx *ctx.Context, where string, args ...interface{}) ([]Role, error) { + var objs []Role + err := DB(ctx).Where(where, args...).Find(&objs).Error + if err != nil { + return nil, errors.WithMessage(err, "failed to query roles") + } + return objs, nil +} + +func RoleGetsAll(ctx *ctx.Context) ([]Role, error) { + return RoleGets(ctx, "") +} + +// 增加角色 +func (r *Role) Add(ctx *ctx.Context) error { + role, err := RoleGet(ctx, "name = ?", r.Name) + if err != nil { + return errors.WithMessage(err, "failed to query user") + } + + if role != nil { + return errors.New("role name already exists") + } + + return DB(ctx).Create(r).Error +} + +// 删除角色 +func (r *Role) Del(ctx *ctx.Context) error { + return DB(ctx).Delete(r).Error +} + +// 更新角色 +func (ug *Role) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { + return DB(ctx).Model(ug).Select(selectField, selectFields...).Updates(ug).Error +} + +func RoleGet(ctx *ctx.Context, where string, args ...interface{}) (*Role, error) { + var lst []*Role + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + return lst[0], nil +} + +func RoleCount(ctx *ctx.Context, where string, args ...interface{}) (num int64, err error) { + return Count(DB(ctx).Model(&Role{}).Where(where, args...)) +} diff --git a/models/role_operation.go b/models/role_operation.go new file mode 100644 index 0000000000000000000000000000000000000000..f5eb1c0ee0149a5a60f29c7bf1a9bf881a335583 --- /dev/null +++ b/models/role_operation.go @@ -0,0 +1,63 @@ +package models + +import ( + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/toolkits/pkg/slice" +) + +type RoleOperation struct { + RoleName string + Operation string +} + +func (RoleOperation) TableName() string { + return "role_operation" +} + +func RoleHasOperation(ctx *ctx.Context, roles []string, operation string) (bool, error) { + if len(roles) == 0 { + return false, nil + } + + return Exists(DB(ctx).Model(&RoleOperation{}).Where("operation = ? and role_name in ?", operation, roles)) +} + +func OperationsOfRole(ctx *ctx.Context, roles []string) ([]string, error) { + session := DB(ctx).Model(&RoleOperation{}).Select("distinct(operation) as operation") + + if !slice.ContainsString(roles, AdminRole) { + session = session.Where("role_name in ?", roles) + } + + var ret []string + err := session.Pluck("operation", &ret).Error + return ret, err +} + +func RoleOperationBind(ctx *ctx.Context, roleName string, operation []string) error { + tx := DB(ctx).Begin() + + if err := tx.Where("role_name = ?", roleName).Delete(&RoleOperation{}).Error; err != nil { + tx.Rollback() + return err + } + + if len(operation) == 0 { + return tx.Commit().Error + } + + var ops []RoleOperation + for _, op := range operation { + ops = append(ops, RoleOperation{ + RoleName: roleName, + Operation: op, + }) + } + + if err := tx.Create(&ops).Error; err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} diff --git a/models/sso_config.go b/models/sso_config.go new file mode 100644 index 0000000000000000000000000000000000000000..e7a26f4580ea4c6cb51a930d36fcccf382c26e74 --- /dev/null +++ b/models/sso_config.go @@ -0,0 +1,44 @@ +package models + +import "github.com/ccfos/nightingale/v6/pkg/ctx" + +// CREATE TABLE `sso_config` ( +// `id` bigint unsigned not null auto_increment, +// `name` varchar(255) not null, +// `content` text not null, +// PRIMARY KEY (`id`), +// UNIQUE KEY (`name`) +// ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +type SsoConfig struct { + Id int64 `json:"id"` + Name string `json:"name"` + Content string `json:"content"` +} + +func (b *SsoConfig) TableName() string { + return "sso_config" +} + +// get all sso_config +func SsoConfigGets(c *ctx.Context) ([]SsoConfig, error) { + var lst []SsoConfig + err := DB(c).Find(&lst).Error + return lst, err +} + +// 创建 builtin_cate +func (b *SsoConfig) Create(c *ctx.Context) error { + return Insert(c, b) +} + +func (b *SsoConfig) Update(c *ctx.Context) error { + return DB(c).Model(b).Select("content").Updates(b).Error +} + +// get sso_config coutn by name +func SsoConfigCountByName(c *ctx.Context, name string) (int64, error) { + var count int64 + err := DB(c).Model(&SsoConfig{}).Where("name = ?", name).Count(&count).Error + return count, err +} diff --git a/models/target.go b/models/target.go new file mode 100644 index 0000000000000000000000000000000000000000..9bfdbc05c800776855af3606ed62268a83147307 --- /dev/null +++ b/models/target.go @@ -0,0 +1,347 @@ +package models + +import ( + "sort" + "strings" + "time" + + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/toolkits/pkg/logger" + + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type Target struct { + Id int64 `json:"id" gorm:"primaryKey"` + GroupId int64 `json:"group_id"` + GroupObj *BusiGroup `json:"group_obj" gorm:"-"` + Cluster string `json:"cluster"` + DatasourceId int64 `json:"datasource_id"` + Ident string `json:"ident"` + Note string `json:"note"` + Tags string `json:"-"` + TagsJSON []string `json:"tags" gorm:"-"` + TagsMap map[string]string `json:"-" gorm:"-"` // internal use, append tags to series + UpdateAt int64 `json:"update_at"` + Offset int64 `json:"offset"` + + TargetUp float64 `json:"target_up" gorm:"-"` + LoadPerCore float64 `json:"load_per_core" gorm:"-"` + MemUtil float64 `json:"mem_util" gorm:"-"` + DiskUtil float64 `json:"disk_util" gorm:"-"` +} + +func (t *Target) TableName() string { + return "target" +} + +func (t *Target) Add(ctx *ctx.Context) error { + obj, err := TargetGet(ctx, "ident = ?", t.Ident) + if err != nil { + return err + } + + if obj == nil { + return Insert(ctx, t) + } + + if obj.DatasourceId != t.DatasourceId { + return DB(ctx).Model(&Target{}).Where("ident = ?", t.Ident).Updates(map[string]interface{}{ + "datasource_id": t.DatasourceId, + "update_at": t.UpdateAt, + }).Error + } + + return nil +} + +func (t *Target) FillGroup(ctx *ctx.Context, cache map[int64]*BusiGroup) error { + if t.GroupId <= 0 { + return nil + } + + bg, has := cache[t.GroupId] + if has { + t.GroupObj = bg + return nil + } + + bg, err := BusiGroupGetById(ctx, t.GroupId) + if err != nil { + return errors.WithMessage(err, "failed to get busi group") + } + + t.GroupObj = bg + cache[t.GroupId] = bg + return nil +} + +func TargetStatistics(ctx *ctx.Context) (*Statistics, error) { + var stats []*Statistics + err := DB(ctx).Model(&Target{}).Select("count(*) as total", "max(update_at) as last_updated").Find(&stats).Error + if err != nil { + return nil, err + } + + return stats[0], nil +} + +func TargetDel(ctx *ctx.Context, idents []string) error { + if len(idents) == 0 { + panic("idents empty") + } + return DB(ctx).Where("ident in ?", idents).Delete(new(Target)).Error +} + +func buildTargetWhere(ctx *ctx.Context, bgid int64, dsIds []int64, query string) *gorm.DB { + session := DB(ctx).Model(&Target{}) + + if bgid >= 0 { + session = session.Where("group_id=?", bgid) + } + + if len(dsIds) > 0 { + session = session.Where("datasource_id in ?", dsIds) + } + + if query != "" { + arr := strings.Fields(query) + for i := 0; i < len(arr); i++ { + q := "%" + arr[i] + "%" + session = session.Where("ident like ? or note like ? or tags like ?", q, q, q) + } + } + + return session +} + +func TargetTotalCount(ctx *ctx.Context) (int64, error) { + return Count(DB(ctx).Model(new(Target))) +} + +func TargetTotal(ctx *ctx.Context, bgid int64, dsIds []int64, query string) (int64, error) { + return Count(buildTargetWhere(ctx, bgid, dsIds, query)) +} + +func TargetGets(ctx *ctx.Context, bgid int64, dsIds []int64, query string, limit, offset int) ([]*Target, error) { + var lst []*Target + err := buildTargetWhere(ctx, bgid, dsIds, query).Order("ident").Limit(limit).Offset(offset).Find(&lst).Error + if err == nil { + for i := 0; i < len(lst); i++ { + lst[i].TagsJSON = strings.Fields(lst[i].Tags) + } + } + return lst, err +} + +// 根据 DatasourceIds, groupids, tags, hosts 查询 targets +func TargetGetsByFilter(ctx *ctx.Context, query map[string]interface{}, typ string, ts int64, limit, offset int) ([]*Target, error) { + var lst []*Target + session := TargetFilterQueryBuild(ctx, query, typ, ts, limit, offset) + err := session.Order("ident").Find(&lst).Error + cache := make(map[int64]*BusiGroup) + for i := 0; i < len(lst); i++ { + lst[i].TagsJSON = strings.Fields(lst[i].Tags) + lst[i].FillGroup(ctx, cache) + } + + return lst, err +} + +func TargetCountByFilter(ctx *ctx.Context, query map[string]interface{}, typ string, ts int64) (int64, error) { + session := TargetFilterQueryBuild(ctx, query, typ, ts, 0, 0) + return Count(session) +} + +func TargetFilterQueryBuild(ctx *ctx.Context, query map[string]interface{}, typ string, ts int64, limit, offset int) *gorm.DB { + session := DB(ctx).Model(&Target{}) + for k, v := range query { + session = session.Where(k, v) + } + + switch typ { + case "target_miss", "pct_target_miss": + session = session.Where("update_at < ?", ts) + case "offset": + session = session.Where("offset > ?", ts) + } + + if limit > 0 { + session = session.Limit(limit).Offset(offset) + } + + return session +} + +func TargetGetsAll(ctx *ctx.Context) ([]*Target, error) { + var lst []*Target + err := DB(ctx).Model(&Target{}).Find(&lst).Error + return lst, err +} + +func TargetUpdateNote(ctx *ctx.Context, idents []string, note string) error { + return DB(ctx).Model(&Target{}).Where("ident in ?", idents).Updates(map[string]interface{}{ + "note": note, + "update_at": time.Now().Unix(), + }).Error +} + +func TargetUpdateBgid(ctx *ctx.Context, idents []string, bgid int64, clearTags bool) error { + fields := map[string]interface{}{ + "group_id": bgid, + "update_at": time.Now().Unix(), + } + + if clearTags { + fields["tags"] = "" + } + + return DB(ctx).Model(&Target{}).Where("ident in ?", idents).Updates(fields).Error +} + +func TargetGet(ctx *ctx.Context, where string, args ...interface{}) (*Target, error) { + var lst []*Target + err := DB(ctx).Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + lst[0].TagsJSON = strings.Fields(lst[0].Tags) + + return lst[0], nil +} + +func TargetGetById(ctx *ctx.Context, id int64) (*Target, error) { + return TargetGet(ctx, "id = ?", id) +} + +func TargetGetByIdent(ctx *ctx.Context, ident string) (*Target, error) { + return TargetGet(ctx, "ident = ?", ident) +} + +func TargetGetTags(ctx *ctx.Context, idents []string) ([]string, error) { + session := DB(ctx).Model(new(Target)) + + var arr []string + if len(idents) > 0 { + session = session.Where("ident in ?", idents) + } + + err := session.Select("distinct(tags) as tags").Pluck("tags", &arr).Error + if err != nil { + return nil, err + } + + cnt := len(arr) + if cnt == 0 { + return []string{}, nil + } + + set := make(map[string]struct{}) + for i := 0; i < cnt; i++ { + tags := strings.Fields(arr[i]) + for j := 0; j < len(tags); j++ { + set[tags[j]] = struct{}{} + } + } + + cnt = len(set) + ret := make([]string, 0, cnt) + for key := range set { + ret = append(ret, key) + } + + sort.Strings(ret) + + return ret, err +} + +func (t *Target) AddTags(ctx *ctx.Context, tags []string) error { + for i := 0; i < len(tags); i++ { + if !strings.Contains(t.Tags, tags[i]+" ") { + t.Tags += tags[i] + " " + } + } + + arr := strings.Fields(t.Tags) + sort.Strings(arr) + + return DB(ctx).Model(t).Updates(map[string]interface{}{ + "tags": strings.Join(arr, " ") + " ", + "update_at": time.Now().Unix(), + }).Error +} + +func (t *Target) DelTags(ctx *ctx.Context, tags []string) error { + for i := 0; i < len(tags); i++ { + t.Tags = strings.ReplaceAll(t.Tags, tags[i]+" ", "") + } + + return DB(ctx).Model(t).Updates(map[string]interface{}{ + "tags": t.Tags, + "update_at": time.Now().Unix(), + }).Error +} + +func TargetIdents(ctx *ctx.Context, ids []int64) ([]string, error) { + var ret []string + + if len(ids) == 0 { + return ret, nil + } + + err := DB(ctx).Model(&Target{}).Where("id in ?", ids).Pluck("ident", &ret).Error + return ret, err +} + +func TargetIds(ctx *ctx.Context, idents []string) ([]int64, error) { + var ret []int64 + + if len(idents) == 0 { + return ret, nil + } + + err := DB(ctx).Model(&Target{}).Where("ident in ?", idents).Pluck("id", &ret).Error + return ret, err +} + +func IdentsFilter(ctx *ctx.Context, idents []string, where string, args ...interface{}) ([]string, error) { + var arr []string + if len(idents) == 0 { + return arr, nil + } + + err := DB(ctx).Model(&Target{}).Where("ident in ?", idents).Where(where, args...).Pluck("ident", &arr).Error + return arr, err +} + +func (m *Target) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error { + return DB(ctx).Model(m).Updates(fields).Error +} + +func TargetUpgradeToV6(ctx *ctx.Context, dsm map[string]Datasource) error { + var lst []*Target + err := DB(ctx).Find(&lst).Error + if err != nil { + return err + } + + for i := 0; i < len(lst); i++ { + ds, exists := dsm[lst[i].Cluster] + if !exists { + continue + } + lst[i].DatasourceId = ds.Id + + err = lst[i].UpdateFieldsMap(ctx, map[string]interface{}{"datasource_id": lst[i].DatasourceId}) + if err != nil { + logger.Errorf("update target:%d datasource ids failed, %v", lst[i].Id, err) + } + } + return nil +} diff --git a/src/models/task_record.go b/models/task_record.go similarity index 63% rename from src/models/task_record.go rename to models/task_record.go index 2a5812f05c6e2ce378db33f4a5a99c76dad129ba..156eab4c2112fe3d07e535a6c3d0b59f403fba8b 100644 --- a/src/models/task_record.go +++ b/models/task_record.go @@ -1,5 +1,7 @@ package models +import "github.com/ccfos/nightingale/v6/pkg/ctx" + type TaskRecord struct { Id int64 `json:"id" gorm:"primaryKey"` GroupId int64 `json:"group_id"` @@ -23,13 +25,13 @@ func (r *TaskRecord) TableName() string { } // create task -func (r *TaskRecord) Add() error { - return Insert(r) +func (r *TaskRecord) Add(ctx *ctx.Context) error { + return Insert(ctx, r) } // list task, filter by group_id, create_by -func TaskRecordTotal(bgid, beginTime int64, createBy, query string) (int64, error) { - session := DB().Model(new(TaskRecord)).Where("create_at > ? and group_id = ?", beginTime, bgid) +func TaskRecordTotal(ctx *ctx.Context, bgid, beginTime int64, createBy, query string) (int64, error) { + session := DB(ctx).Model(new(TaskRecord)).Where("create_at > ? and group_id = ?", beginTime, bgid) if createBy != "" { session = session.Where("create_by = ?", createBy) @@ -42,8 +44,8 @@ func TaskRecordTotal(bgid, beginTime int64, createBy, query string) (int64, erro return Count(session) } -func TaskRecordGets(bgid, beginTime int64, createBy, query string, limit, offset int) ([]*TaskRecord, error) { - session := DB().Where("create_at > ? and group_id = ?", beginTime, bgid).Order("create_at desc").Limit(limit).Offset(offset) +func TaskRecordGets(ctx *ctx.Context, bgid, beginTime int64, createBy, query string, limit, offset int) ([]*TaskRecord, error) { + session := DB(ctx).Where("create_at > ? and group_id = ?", beginTime, bgid).Order("create_at desc").Limit(limit).Offset(offset) if createBy != "" { session = session.Where("create_by = ?", createBy) @@ -59,6 +61,6 @@ func TaskRecordGets(bgid, beginTime int64, createBy, query string, limit, offset } // update is_done field -func (r *TaskRecord) UpdateIsDone(isDone int) error { - return DB().Model(r).Update("is_done", isDone).Error +func (r *TaskRecord) UpdateIsDone(ctx *ctx.Context, isDone int) error { + return DB(ctx).Model(r).Update("is_done", isDone).Error } diff --git a/src/models/task_tpl.go b/models/task_tpl.go similarity index 74% rename from src/models/task_tpl.go rename to models/task_tpl.go index 53ed0dc5bb66b8bf063a3a7b39f6c67aad239086..501e6750a1a3778d6e10fdc0bd8abeea24b5d6e4 100644 --- a/src/models/task_tpl.go +++ b/models/task_tpl.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" "github.com/toolkits/pkg/str" "gorm.io/gorm" ) @@ -34,8 +35,8 @@ func (t *TaskTpl) TableName() string { return "task_tpl" } -func TaskTplTotal(groupId int64, query string) (int64, error) { - session := DB().Model(&TaskTpl{}).Where("group_id = ?", groupId) +func TaskTplTotal(ctx *ctx.Context, groupId int64, query string) (int64, error) { + session := DB(ctx).Model(&TaskTpl{}).Where("group_id = ?", groupId) if query == "" { return Count(session) } @@ -49,8 +50,8 @@ func TaskTplTotal(groupId int64, query string) (int64, error) { return Count(session) } -func TaskTplGets(groupId int64, query string, limit, offset int) ([]TaskTpl, error) { - session := DB().Where("group_id = ?", groupId).Order("title").Limit(limit).Offset(offset) +func TaskTplGets(ctx *ctx.Context, groupId int64, query string, limit, offset int) ([]TaskTpl, error) { + session := DB(ctx).Where("group_id = ?", groupId).Order("title").Limit(limit).Offset(offset) var tpls []TaskTpl if query != "" { @@ -71,9 +72,9 @@ func TaskTplGets(groupId int64, query string, limit, offset int) ([]TaskTpl, err return tpls, err } -func TaskTplGet(where string, args ...interface{}) (*TaskTpl, error) { +func TaskTplGet(ctx *ctx.Context, where string, args ...interface{}) (*TaskTpl, error) { var arr []*TaskTpl - err := DB().Where(where, args...).Find(&arr).Error + err := DB(ctx).Where(where, args...).Find(&arr).Error if err != nil { return nil, err } @@ -140,12 +141,12 @@ func (t *TaskTpl) CleanFields() error { return nil } -func (t *TaskTpl) Save(hosts []string) error { +func (t *TaskTpl) Save(ctx *ctx.Context, hosts []string) error { if err := t.CleanFields(); err != nil { return err } - cnt, err := Count(DB().Model(&TaskTpl{}).Where("group_id=? and title=?", t.GroupId, t.Title)) + cnt, err := Count(DB(ctx).Model(&TaskTpl{}).Where("group_id=? and title=?", t.GroupId, t.Title)) if err != nil { return err } @@ -154,7 +155,7 @@ func (t *TaskTpl) Save(hosts []string) error { return fmt.Errorf("task template already exists") } - return DB().Transaction(func(tx *gorm.DB) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Create(t).Error; err != nil { return err } @@ -179,18 +180,18 @@ func (t *TaskTpl) Save(hosts []string) error { }) } -func (t *TaskTpl) Hosts() ([]string, error) { +func (t *TaskTpl) Hosts(ctx *ctx.Context) ([]string, error) { var arr []string - err := DB().Table("task_tpl_host").Where("id=?", t.Id).Order("ii").Pluck("host", &arr).Error + err := DB(ctx).Table("task_tpl_host").Where("id=?", t.Id).Order("ii").Pluck("host", &arr).Error return arr, err } -func (t *TaskTpl) Update(hosts []string) error { +func (t *TaskTpl) Update(ctx *ctx.Context, hosts []string) error { if err := t.CleanFields(); err != nil { return err } - cnt, err := Count(DB().Model(&TaskTpl{}).Where("group_id=? and title=? and id <> ?", t.GroupId, t.Title, t.Id)) + cnt, err := Count(DB(ctx).Model(&TaskTpl{}).Where("group_id=? and title=? and id <> ?", t.GroupId, t.Title, t.Id)) if err != nil { return err } @@ -199,7 +200,7 @@ func (t *TaskTpl) Update(hosts []string) error { return fmt.Errorf("task template already exists") } - return DB().Transaction(func(tx *gorm.DB) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { err := tx.Model(t).Updates(map[string]interface{}{ "title": t.Title, "batch": t.Batch, @@ -242,8 +243,8 @@ func (t *TaskTpl) Update(hosts []string) error { }) } -func (t *TaskTpl) Del() error { - return DB().Transaction(func(tx *gorm.DB) error { +func (t *TaskTpl) Del(ctx *ctx.Context) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Exec("DELETE FROM task_tpl_host WHERE id=?", t.Id).Error; err != nil { return err } @@ -256,7 +257,7 @@ func (t *TaskTpl) Del() error { }) } -func (t *TaskTpl) AddTags(tags []string, updateBy string) error { +func (t *TaskTpl) AddTags(ctx *ctx.Context, tags []string, updateBy string) error { for i := 0; i < len(tags); i++ { if -1 == strings.Index(t.Tags, tags[i]+" ") { t.Tags += tags[i] + " " @@ -266,27 +267,27 @@ func (t *TaskTpl) AddTags(tags []string, updateBy string) error { arr := strings.Fields(t.Tags) sort.Strings(arr) - return DB().Model(t).Updates(map[string]interface{}{ + return DB(ctx).Model(t).Updates(map[string]interface{}{ "tags": strings.Join(arr, " ") + " ", "update_by": updateBy, "update_at": time.Now().Unix(), }).Error } -func (t *TaskTpl) DelTags(tags []string, updateBy string) error { +func (t *TaskTpl) DelTags(ctx *ctx.Context, tags []string, updateBy string) error { for i := 0; i < len(tags); i++ { t.Tags = strings.ReplaceAll(t.Tags, tags[i]+" ", "") } - return DB().Model(t).Updates(map[string]interface{}{ + return DB(ctx).Model(t).Updates(map[string]interface{}{ "tags": t.Tags, "update_by": updateBy, "update_at": time.Now().Unix(), }).Error } -func (t *TaskTpl) UpdateGroup(groupId int64, updateBy string) error { - return DB().Model(t).Updates(map[string]interface{}{ +func (t *TaskTpl) UpdateGroup(ctx *ctx.Context, groupId int64, updateBy string) error { + return DB(ctx).Model(t).Updates(map[string]interface{}{ "group_id": groupId, "update_by": updateBy, "update_at": time.Now().Unix(), diff --git a/src/models/user.go b/models/user.go similarity index 65% rename from src/models/user.go rename to models/user.go index 1fdefa0775983be86ff39b2cc89027c98181909a..4f25e86ed38e60124ff0425959823863ed7f0cad 100644 --- a/src/models/user.go +++ b/models/user.go @@ -6,25 +6,26 @@ import ( "strings" "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/ldapx" + "github.com/ccfos/nightingale/v6/pkg/ormx" + "github.com/pkg/errors" "github.com/tidwall/gjson" "github.com/toolkits/pkg/logger" "github.com/toolkits/pkg/slice" "github.com/toolkits/pkg/str" "gorm.io/gorm" - - "github.com/didi/nightingale/v5/src/pkg/ldapx" - "github.com/didi/nightingale/v5/src/pkg/ormx" - "github.com/didi/nightingale/v5/src/webapi/config" ) const ( - Dingtalk = "dingtalk" - Wecom = "wecom" - Feishu = "feishu" - Mm = "mm" - Telegram = "telegram" - Email = "email" + Dingtalk = "dingtalk" + Wecom = "wecom" + Feishu = "feishu" + Mm = "mm" + Telegram = "telegram" + Email = "email" + EmailSubject = "mailsubject" DingtalkKey = "dingtalk_robot_token" WecomKey = "wecom_robot_token" @@ -91,8 +92,8 @@ func (u *User) Verify() error { return nil } -func (u *User) Add() error { - user, err := UserGetByUsername(u.Username) +func (u *User) Add(ctx *ctx.Context) error { + user, err := UserGetByUsername(ctx, u.Username) if err != nil { return errors.WithMessage(err, "failed to query user") } @@ -104,36 +105,36 @@ func (u *User) Add() error { now := time.Now().Unix() u.CreateAt = now u.UpdateAt = now - return Insert(u) + return Insert(ctx, u) } -func (u *User) Update(selectField interface{}, selectFields ...interface{}) error { +func (u *User) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { if err := u.Verify(); err != nil { return err } - return DB().Model(u).Select(selectField, selectFields...).Updates(u).Error + return DB(ctx).Model(u).Select(selectField, selectFields...).Updates(u).Error } -func (u *User) UpdateAllFields() error { +func (u *User) UpdateAllFields(ctx *ctx.Context) error { if err := u.Verify(); err != nil { return err } u.UpdateAt = time.Now().Unix() - return DB().Model(u).Select("*").Updates(u).Error + return DB(ctx).Model(u).Select("*").Updates(u).Error } -func (u *User) UpdatePassword(password, updateBy string) error { - return DB().Model(u).Updates(map[string]interface{}{ +func (u *User) UpdatePassword(ctx *ctx.Context, password, updateBy string) error { + return DB(ctx).Model(u).Updates(map[string]interface{}{ "password": password, "update_at": time.Now().Unix(), "update_by": updateBy, }).Error } -func (u *User) Del() error { - return DB().Transaction(func(tx *gorm.DB) error { +func (u *User) Del(ctx *ctx.Context) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("user_id=?", u.Id).Delete(&UserGroupMember{}).Error; err != nil { return err } @@ -146,13 +147,13 @@ func (u *User) Del() error { }) } -func (u *User) ChangePassword(oldpass, newpass string) error { - _oldpass, err := CryptoPass(oldpass) +func (u *User) ChangePassword(ctx *ctx.Context, oldpass, newpass string) error { + _oldpass, err := CryptoPass(ctx, oldpass) if err != nil { return err } - _newpass, err := CryptoPass(newpass) + _newpass, err := CryptoPass(ctx, newpass) if err != nil { return err } @@ -161,12 +162,12 @@ func (u *User) ChangePassword(oldpass, newpass string) error { return errors.New("Incorrect old password") } - return u.UpdatePassword(_newpass, u.Username) + return u.UpdatePassword(ctx, _newpass, u.Username) } -func UserGet(where string, args ...interface{}) (*User, error) { +func UserGet(ctx *ctx.Context, where string, args ...interface{}) (*User, error) { var lst []*User - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } @@ -181,16 +182,16 @@ func UserGet(where string, args ...interface{}) (*User, error) { return lst[0], nil } -func UserGetByUsername(username string) (*User, error) { - return UserGet("username=?", username) +func UserGetByUsername(ctx *ctx.Context, username string) (*User, error) { + return UserGet(ctx, "username=?", username) } -func UserGetById(id int64) (*User, error) { - return UserGet("id=?", id) +func UserGetById(ctx *ctx.Context, id int64) (*User, error) { + return UserGet(ctx, "id=?", id) } -func InitRoot() { - user, err := UserGetByUsername("root") +func InitRoot(ctx *ctx.Context) { + user, err := UserGetByUsername(ctx, "root") if err != nil { fmt.Println("failed to query user root:", err) os.Exit(1) @@ -205,13 +206,13 @@ func InitRoot() { return } - newPass, err := CryptoPass(user.Password) + newPass, err := CryptoPass(ctx, user.Password) if err != nil { fmt.Println("failed to crypto pass:", err) os.Exit(1) } - err = DB().Model(user).Update("password", newPass).Error + err = DB(ctx).Model(user).Update("password", newPass).Error if err != nil { fmt.Println("failed to update root password:", err) os.Exit(1) @@ -220,8 +221,8 @@ func InitRoot() { fmt.Println("root password init done") } -func PassLogin(username, pass string) (*User, error) { - user, err := UserGetByUsername(username) +func PassLogin(ctx *ctx.Context, username, pass string) (*User, error) { + user, err := UserGetByUsername(ctx, username) if err != nil { return nil, err } @@ -230,7 +231,7 @@ func PassLogin(username, pass string) (*User, error) { return nil, fmt.Errorf("Username or password invalid") } - loginPass, err := CryptoPass(pass) + loginPass, err := CryptoPass(ctx, pass) if err != nil { return nil, err } @@ -242,13 +243,13 @@ func PassLogin(username, pass string) (*User, error) { return user, nil } -func LdapLogin(username, pass string) (*User, error) { - sr, err := ldapx.LdapReq(username, pass) +func LdapLogin(ctx *ctx.Context, username, pass, roles string, ldap *ldapx.SsoClient) (*User, error) { + sr, err := ldap.LdapReq(username, pass) if err != nil { return nil, err } - user, err := UserGetByUsername(username) + user, err := UserGetByUsername(ctx, username) if err != nil { return nil, err } @@ -262,7 +263,11 @@ func LdapLogin(username, pass string) (*User, error) { } // copy attributes from ldap - attrs := ldapx.LDAP.Attributes + ldap.RLock() + attrs := ldap.Attributes + coverAttributes := ldap.CoverAttributes + ldap.RUnlock() + if attrs.Nickname != "" { user.Nickname = sr.Entries[0].GetAttributeValue(attrs.Nickname) } @@ -273,9 +278,11 @@ func LdapLogin(username, pass string) (*User, error) { user.Phone = sr.Entries[0].GetAttributeValue(attrs.Phone) } + user.Roles = roles + if user.Id > 0 { - if ldapx.LDAP.CoverAttributes { - err := DB().Updates(user).Error + if coverAttributes { + err := DB(ctx).Updates(user).Error if err != nil { return nil, errors.WithMessage(err, "failed to update user") } @@ -285,30 +292,25 @@ func LdapLogin(username, pass string) (*User, error) { now := time.Now().Unix() - if len(config.C.LDAP.DefaultRoles) == 0 { - config.C.LDAP.DefaultRoles = []string{"Standard"} - } - user.Password = "******" user.Portrait = "" - user.Roles = strings.Join(config.C.LDAP.DefaultRoles, " ") - user.RolesLst = config.C.LDAP.DefaultRoles + user.Contacts = []byte("{}") user.CreateAt = now user.UpdateAt = now user.CreateBy = "ldap" user.UpdateBy = "ldap" - err = DB().Create(user).Error + err = DB(ctx).Create(user).Error return user, err } -func UserTotal(query string) (num int64, err error) { +func UserTotal(ctx *ctx.Context, query string) (num int64, err error) { if query != "" { q := "%" + query + "%" - num, err = Count(DB().Model(&User{}).Where("username like ? or nickname like ? or phone like ? or email like ?", q, q, q, q)) + num, err = Count(DB(ctx).Model(&User{}).Where("username like ? or nickname like ? or phone like ? or email like ?", q, q, q, q)) } else { - num, err = Count(DB().Model(&User{})) + num, err = Count(DB(ctx).Model(&User{})) } if err != nil { @@ -318,8 +320,8 @@ func UserTotal(query string) (num int64, err error) { return num, nil } -func UserGets(query string, limit, offset int) ([]User, error) { - session := DB().Limit(limit).Offset(offset).Order("username") +func UserGets(ctx *ctx.Context, query string, limit, offset int) ([]User, error) { + session := DB(ctx).Limit(limit).Offset(offset).Order("username") if query != "" { q := "%" + query + "%" session = session.Where("username like ? or nickname like ? or phone like ? or email like ?", q, q, q, q) @@ -339,9 +341,9 @@ func UserGets(query string, limit, offset int) ([]User, error) { return users, nil } -func UserGetAll() ([]*User, error) { +func UserGetAll(ctx *ctx.Context) ([]*User, error) { var lst []*User - err := DB().Find(&lst).Error + err := DB(ctx).Find(&lst).Error if err == nil { for i := 0; i < len(lst); i++ { lst[i].RolesLst = strings.Fields(lst[i].Roles) @@ -351,13 +353,13 @@ func UserGetAll() ([]*User, error) { return lst, err } -func UserGetsByIds(ids []int64) ([]User, error) { +func UserGetsByIds(ctx *ctx.Context, ids []int64) ([]User, error) { if len(ids) == 0 { return []User{}, nil } var lst []User - err := DB().Where("id in ?", ids).Order("username").Find(&lst).Error + err := DB(ctx).Where("id in ?", ids).Order("username").Find(&lst).Error if err == nil { for i := 0; i < len(lst); i++ { lst[i].RolesLst = strings.Fields(lst[i].Roles) @@ -368,7 +370,7 @@ func UserGetsByIds(ids []int64) ([]User, error) { return lst, err } -func (u *User) CanModifyUserGroup(ug *UserGroup) (bool, error) { +func (u *User) CanModifyUserGroup(ctx *ctx.Context, ug *UserGroup) (bool, error) { // 我是管理员,自然可以 if u.IsAdmin() { return true, nil @@ -380,7 +382,7 @@ func (u *User) CanModifyUserGroup(ug *UserGroup) (bool, error) { } // 我是成员,也可以吧,简单搞 - num, err := UserGroupMemberCount("user_id=? and group_id=?", u.Id, ug.Id) + num, err := UserGroupMemberCount(ctx, "user_id=? and group_id=?", u.Id, ug.Id) if err != nil { return false, err } @@ -388,13 +390,13 @@ func (u *User) CanModifyUserGroup(ug *UserGroup) (bool, error) { return num > 0, nil } -func (u *User) CanDoBusiGroup(bg *BusiGroup, permFlag ...string) (bool, error) { +func (u *User) CanDoBusiGroup(ctx *ctx.Context, bg *BusiGroup, permFlag ...string) (bool, error) { if u.IsAdmin() { return true, nil } // 我在任意一个UserGroup里,就有权限 - ugids, err := UserGroupIdsOfBusiGroup(bg.Id, permFlag...) + ugids, err := UserGroupIdsOfBusiGroup(ctx, bg.Id, permFlag...) if err != nil { return false, err } @@ -403,20 +405,20 @@ func (u *User) CanDoBusiGroup(bg *BusiGroup, permFlag ...string) (bool, error) { return false, nil } - num, err := UserGroupMemberCount("user_id = ? and group_id in ?", u.Id, ugids) + num, err := UserGroupMemberCount(ctx, "user_id = ? and group_id in ?", u.Id, ugids) return num > 0, err } -func (u *User) CheckPerm(operation string) (bool, error) { +func (u *User) CheckPerm(ctx *ctx.Context, operation string) (bool, error) { if u.IsAdmin() { return true, nil } - return RoleHasOperation(u.RolesLst, operation) + return RoleHasOperation(ctx, u.RolesLst, operation) } -func UserStatistics() (*Statistics, error) { - session := DB().Model(&User{}).Select("count(*) as total", "max(update_at) as last_updated") +func UserStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&User{}).Select("count(*) as total", "max(update_at) as last_updated") var stats []*Statistics err := session.Find(&stats).Error @@ -427,12 +429,12 @@ func UserStatistics() (*Statistics, error) { return stats[0], nil } -func (u *User) NopriIdents(idents []string) ([]string, error) { +func (u *User) NopriIdents(ctx *ctx.Context, idents []string) ([]string, error) { if u.IsAdmin() { return []string{}, nil } - ugids, err := MyGroupIds(u.Id) + ugids, err := MyGroupIds(ctx, u.Id) if err != nil { return []string{}, err } @@ -441,7 +443,7 @@ func (u *User) NopriIdents(idents []string) ([]string, error) { return idents, nil } - bgids, err := BusiGroupIds(ugids, "rw") + bgids, err := BusiGroupIds(ctx, ugids, "rw") if err != nil { return []string{}, err } @@ -451,7 +453,7 @@ func (u *User) NopriIdents(idents []string) ([]string, error) { } var arr []string - err = DB().Model(&Target{}).Where("group_id in ?", bgids).Pluck("ident", &arr).Error + err = DB(ctx).Model(&Target{}).Where("group_id in ?", bgids).Pluck("ident", &arr).Error if err != nil { return []string{}, err } @@ -461,8 +463,8 @@ func (u *User) NopriIdents(idents []string) ([]string, error) { // 我是管理员,返回所有 // 或者我是成员 -func (u *User) BusiGroups(limit int, query string, all ...bool) ([]BusiGroup, error) { - session := DB().Order("name").Limit(limit) +func (u *User) BusiGroups(ctx *ctx.Context, limit int, query string, all ...bool) ([]BusiGroup, error) { + session := DB(ctx).Order("name").Limit(limit) var lst []BusiGroup if u.IsAdmin() || (len(all) > 0 && all[0]) { @@ -474,7 +476,7 @@ func (u *User) BusiGroups(limit int, query string, all ...bool) ([]BusiGroup, er if len(lst) == 0 && len(query) > 0 { // 隐藏功能,一般人不告诉,哈哈。query可能是给的ident,所以上面的sql没有查到,当做ident来查一下试试 var t *Target - t, err = TargetGet("ident=?", query) + t, err = TargetGet(ctx, "ident=?", query) if err != nil { return lst, err } @@ -483,18 +485,18 @@ func (u *User) BusiGroups(limit int, query string, all ...bool) ([]BusiGroup, er return lst, nil } - err = DB().Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error + err = DB(ctx).Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error } return lst, err } - userGroupIds, err := MyGroupIds(u.Id) + userGroupIds, err := MyGroupIds(ctx, u.Id) if err != nil { return nil, errors.WithMessage(err, "failed to get MyGroupIds") } - busiGroupIds, err := BusiGroupIds(userGroupIds) + busiGroupIds, err := BusiGroupIds(ctx, userGroupIds) if err != nil { return nil, errors.WithMessage(err, "failed to get BusiGroupIds") } @@ -510,21 +512,21 @@ func (u *User) BusiGroups(limit int, query string, all ...bool) ([]BusiGroup, er if len(lst) == 0 && len(query) > 0 { var t *Target - t, err = TargetGet("ident=?", query) + t, err = TargetGet(ctx, "ident=?", query) if err != nil { return lst, err } if slice.ContainsInt64(busiGroupIds, t.GroupId) { - err = DB().Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error + err = DB(ctx).Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error } } return lst, err } -func (u *User) UserGroups(limit int, query string) ([]UserGroup, error) { - session := DB().Order("name").Limit(limit) +func (u *User) UserGroups(ctx *ctx.Context, limit int, query string) ([]UserGroup, error) { + session := DB(ctx).Order("name").Limit(limit) var lst []UserGroup if u.IsAdmin() { @@ -535,21 +537,21 @@ func (u *User) UserGroups(limit int, query string) ([]UserGroup, error) { if len(lst) == 0 && len(query) > 0 { // 隐藏功能,一般人不告诉,哈哈。query可能是给的用户名,所以上面的sql没有查到,当做user来查一下试试 - user, err := UserGetByUsername(query) + user, err := UserGetByUsername(ctx, query) if user == nil { return lst, err } var ids []int64 - ids, err = MyGroupIds(user.Id) + ids, err = MyGroupIds(ctx, user.Id) if err != nil || len(ids) == 0 { return lst, err } - lst, err = UserGroupGetByIds(ids) + lst, err = UserGroupGetByIds(ctx, ids) } return lst, err } - ids, err := MyGroupIds(u.Id) + ids, err := MyGroupIds(ctx, u.Id) if err != nil { return nil, errors.WithMessage(err, "failed to get MyGroupIds") } @@ -560,7 +562,11 @@ func (u *User) UserGroups(limit int, query string) ([]UserGroup, error) { session = session.Where("create_by = ?", u.Username) } - err = session.Where("name like ?", "%"+query+"%").Find(&lst).Error + if len(query) > 0 { + session = session.Where("name like ?", "%"+query+"%") + } + + err = session.Find(&lst).Error return lst, err } @@ -593,23 +599,3 @@ func (u *User) ExtractToken(key string) (string, bool) { return "", false } } - -func (u *User) ExtractAllToken() map[string]string { - ret := make(map[string]string) - if u.Email != "" { - ret[Email] = u.Email - } - - bs, err := u.Contacts.MarshalJSON() - if err != nil { - logger.Errorf("handle_notice: failed to marshal contacts: %v", err) - return ret - } - - ret[Dingtalk] = gjson.GetBytes(bs, DingtalkKey).String() - ret[Wecom] = gjson.GetBytes(bs, WecomKey).String() - ret[Feishu] = gjson.GetBytes(bs, FeishuKey).String() - ret[Mm] = gjson.GetBytes(bs, MmKey).String() - ret[Telegram] = gjson.GetBytes(bs, TelegramKey).String() - return ret -} diff --git a/src/models/user_group.go b/models/user_group.go similarity index 53% rename from src/models/user_group.go rename to models/user_group.go index 0f95c8bdd22283e85c366c673ba4b49a6467d1e3..137c1df2fba3b4363595e321c332d93d3dd3284d 100644 --- a/src/models/user_group.go +++ b/models/user_group.go @@ -3,6 +3,7 @@ package models import ( "time" + "github.com/ccfos/nightingale/v6/pkg/ctx" "github.com/pkg/errors" "github.com/toolkits/pkg/str" "gorm.io/gorm" @@ -35,24 +36,24 @@ func (ug *UserGroup) Verify() error { return nil } -func (ug *UserGroup) Update(selectField interface{}, selectFields ...interface{}) error { +func (ug *UserGroup) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { if err := ug.Verify(); err != nil { return err } - return DB().Model(ug).Select(selectField, selectFields...).Updates(ug).Error + return DB(ctx).Model(ug).Select(selectField, selectFields...).Updates(ug).Error } -func UserGroupCount(where string, args ...interface{}) (num int64, err error) { - return Count(DB().Model(&UserGroup{}).Where(where, args...)) +func UserGroupCount(ctx *ctx.Context, where string, args ...interface{}) (num int64, err error) { + return Count(DB(ctx).Model(&UserGroup{}).Where(where, args...)) } -func (ug *UserGroup) Add() error { +func (ug *UserGroup) Add(ctx *ctx.Context) error { if err := ug.Verify(); err != nil { return err } - num, err := UserGroupCount("name=?", ug.Name) + num, err := UserGroupCount(ctx, "name=?", ug.Name) if err != nil { return errors.WithMessage(err, "failed to count user-groups") } @@ -64,11 +65,11 @@ func (ug *UserGroup) Add() error { now := time.Now().Unix() ug.CreateAt = now ug.UpdateAt = now - return Insert(ug) + return Insert(ctx, ug) } -func (ug *UserGroup) Del() error { - return DB().Transaction(func(tx *gorm.DB) error { +func (ug *UserGroup) Del(ctx *ctx.Context) error { + return DB(ctx).Transaction(func(tx *gorm.DB) error { if err := tx.Where("group_id=?", ug.Id).Delete(&UserGroupMember{}).Error; err != nil { return err } @@ -81,9 +82,9 @@ func (ug *UserGroup) Del() error { }) } -func UserGroupGet(where string, args ...interface{}) (*UserGroup, error) { +func UserGroupGet(ctx *ctx.Context, where string, args ...interface{}) (*UserGroup, error) { var lst []*UserGroup - err := DB().Where(where, args...).Find(&lst).Error + err := DB(ctx).Where(where, args...).Find(&lst).Error if err != nil { return nil, err } @@ -95,37 +96,37 @@ func UserGroupGet(where string, args ...interface{}) (*UserGroup, error) { return lst[0], nil } -func UserGroupGetById(id int64) (*UserGroup, error) { - return UserGroupGet("id = ?", id) +func UserGroupGetById(ctx *ctx.Context, id int64) (*UserGroup, error) { + return UserGroupGet(ctx, "id = ?", id) } -func UserGroupGetByIds(ids []int64) ([]UserGroup, error) { +func UserGroupGetByIds(ctx *ctx.Context, ids []int64) ([]UserGroup, error) { var lst []UserGroup if len(ids) == 0 { return lst, nil } - err := DB().Where("id in ?", ids).Order("name").Find(&lst).Error + err := DB(ctx).Where("id in ?", ids).Order("name").Find(&lst).Error return lst, err } -func UserGroupGetAll() ([]*UserGroup, error) { +func UserGroupGetAll(ctx *ctx.Context) ([]*UserGroup, error) { var lst []*UserGroup - err := DB().Find(&lst).Error + err := DB(ctx).Find(&lst).Error return lst, err } -func (ug *UserGroup) AddMembers(userIds []int64) error { +func (ug *UserGroup) AddMembers(ctx *ctx.Context, userIds []int64) error { count := len(userIds) for i := 0; i < count; i++ { - user, err := UserGetById(userIds[i]) + user, err := UserGetById(ctx, userIds[i]) if err != nil { return err } if user == nil { continue } - err = UserGroupMemberAdd(ug.Id, user.Id) + err = UserGroupMemberAdd(ctx, ug.Id, user.Id) if err != nil { return err } @@ -133,12 +134,12 @@ func (ug *UserGroup) AddMembers(userIds []int64) error { return nil } -func (ug *UserGroup) DelMembers(userIds []int64) error { - return UserGroupMemberDel(ug.Id, userIds) +func (ug *UserGroup) DelMembers(ctx *ctx.Context, userIds []int64) error { + return UserGroupMemberDel(ctx, ug.Id, userIds) } -func UserGroupStatistics() (*Statistics, error) { - session := DB().Model(&UserGroup{}).Select("count(*) as total", "max(update_at) as last_updated") +func UserGroupStatistics(ctx *ctx.Context) (*Statistics, error) { + session := DB(ctx).Model(&UserGroup{}).Select("count(*) as total", "max(update_at) as last_updated") var stats []*Statistics err := session.Find(&stats).Error diff --git a/models/user_group_member.go b/models/user_group_member.go new file mode 100644 index 0000000000000000000000000000000000000000..cf3216f5f66391cb6392dde44604eae6b62c3356 --- /dev/null +++ b/models/user_group_member.go @@ -0,0 +1,61 @@ +package models + +import "github.com/ccfos/nightingale/v6/pkg/ctx" + +type UserGroupMember struct { + GroupId int64 + UserId int64 +} + +func (UserGroupMember) TableName() string { + return "user_group_member" +} + +func MyGroupIds(ctx *ctx.Context, userId int64) ([]int64, error) { + var ids []int64 + err := DB(ctx).Model(&UserGroupMember{}).Where("user_id=?", userId).Pluck("group_id", &ids).Error + return ids, err +} + +func MemberIds(ctx *ctx.Context, groupId int64) ([]int64, error) { + var ids []int64 + err := DB(ctx).Model(&UserGroupMember{}).Where("group_id=?", groupId).Pluck("user_id", &ids).Error + return ids, err +} + +func UserGroupMemberCount(ctx *ctx.Context, where string, args ...interface{}) (int64, error) { + return Count(DB(ctx).Model(&UserGroupMember{}).Where(where, args...)) +} + +func UserGroupMemberAdd(ctx *ctx.Context, groupId, userId int64) error { + num, err := UserGroupMemberCount(ctx, "user_id=? and group_id=?", userId, groupId) + if err != nil { + return err + } + + if num > 0 { + // already exists + return nil + } + + obj := UserGroupMember{ + GroupId: groupId, + UserId: userId, + } + + return Insert(ctx, obj) +} + +func UserGroupMemberDel(ctx *ctx.Context, groupId int64, userIds []int64) error { + if len(userIds) == 0 { + return nil + } + + return DB(ctx).Where("group_id = ? and user_id in ?", groupId, userIds).Delete(&UserGroupMember{}).Error +} + +func UserGroupMemberGetAll(ctx *ctx.Context) ([]UserGroupMember, error) { + var lst []UserGroupMember + err := DB(ctx).Find(&lst).Error + return lst, err +} diff --git a/docker/initsql/a-n9e.sql b/n9e.sql similarity index 80% rename from docker/initsql/a-n9e.sql rename to n9e.sql index 0ca129d319b4c70035f903c109c54b5621d662be..68847cbf3f6c8eccd5f25f1ae38b9821177198b5 100644 --- a/docker/initsql/a-n9e.sql +++ b/n9e.sql @@ -1,8 +1,8 @@ set names utf8mb4; -drop database if exists n9e_v5; -create database n9e_v5; -use n9e_v5; +drop database if exists n9e_v6; +create database n9e_v6; +use n9e_v6; CREATE TABLE `users` ( `id` bigint unsigned not null auto_increment, @@ -83,12 +83,21 @@ CREATE TABLE `role_operation`( -- Admin is special, who has no concrete operation but can do anything. insert into `role_operation`(role_name, operation) values('Guest', '/metric/explorer'); insert into `role_operation`(role_name, operation) values('Guest', '/object/explorer'); +insert into `role_operation`(role_name, operation) values('Guest', '/log/explorer'); +insert into `role_operation`(role_name, operation) values('Guest', '/trace/explorer'); insert into `role_operation`(role_name, operation) values('Guest', '/help/version'); insert into `role_operation`(role_name, operation) values('Guest', '/help/contact'); + insert into `role_operation`(role_name, operation) values('Standard', '/metric/explorer'); insert into `role_operation`(role_name, operation) values('Standard', '/object/explorer'); +insert into `role_operation`(role_name, operation) values('Standard', '/log/explorer'); +insert into `role_operation`(role_name, operation) values('Standard', '/trace/explorer'); insert into `role_operation`(role_name, operation) values('Standard', '/help/version'); insert into `role_operation`(role_name, operation) values('Standard', '/help/contact'); +insert into `role_operation`(role_name, operation) values('Standard', '/alert-rules-built-in'); +insert into `role_operation`(role_name, operation) values('Standard', '/dashboards-built-in'); +insert into `role_operation`(role_name, operation) values('Standard', '/trace/dependencies'); + insert into `role_operation`(role_name, operation) values('Standard', '/users'); insert into `role_operation`(role_name, operation) values('Standard', '/user-groups'); insert into `role_operation`(role_name, operation) values('Standard', '/user-groups/add'); @@ -168,6 +177,8 @@ CREATE TABLE `board` ( `ident` varchar(200) not null default '', `tags` varchar(255) not null comment 'split by space', `public` tinyint(1) not null default 0 comment '0:false 1:true', + `built_in` tinyint(1) not null default 0 comment '0:false 1:true', + `hide` tinyint(1) not null default 0 comment '0:false 1:true', `create_at` bigint not null default 0, `create_by` varchar(64) not null default '', `update_at` bigint not null default 0, @@ -177,6 +188,9 @@ CREATE TABLE `board` ( KEY(`ident`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +alter table board add built_in tinyint(1) not null default 0 comment '0:false 1:true'; +alter table board add hide tinyint(1) not null default 0 comment '0:false 1:true'; + -- for dashboard new version CREATE TABLE `board_payload` ( `id` bigint unsigned not null comment 'dashboard id', @@ -223,6 +237,7 @@ CREATE TABLE `chart` ( CREATE TABLE `chart_share` ( `id` bigint unsigned not null auto_increment, `cluster` varchar(128) not null, + `dashboard_id` bigint unsigned not null, `configs` text, `create_at` bigint not null default 0, `create_by` varchar(64) not null default '', @@ -230,10 +245,13 @@ CREATE TABLE `chart_share` ( key (`create_at`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +alter table chart_share add datasource_id bigint unsigned not null default 0; + CREATE TABLE `alert_rule` ( `id` bigint unsigned not null auto_increment, `group_id` bigint not null default 0 comment 'busi group id', `cate` varchar(128) not null, + `datasource_ids` varchar(255) not null default '' comment 'datasource ids', `cluster` varchar(128) not null, `name` varchar(255) not null, `note` varchar(1024) not null default '', @@ -244,11 +262,12 @@ CREATE TABLE `alert_rule` ( `severity` tinyint(1) not null comment '1:Emergency 2:Warning 3:Notice', `disabled` tinyint(1) not null comment '0:enabled 1:disabled', `prom_for_duration` int not null comment 'prometheus for, unit:s', + `rule_config` text not null comment 'rule_config'; `prom_ql` text not null comment 'promql', `prom_eval_interval` int not null comment 'evaluate interval', - `enable_stime` char(255) not null default '00:00', - `enable_etime` char(255) not null default '23:59', - `enable_days_of_week` varchar(255) not null default '' comment 'eg: "0 1 2 3 4 5 6 ; 0 1 2"', + `enable_stime` varchar(255) not null default '00:00', + `enable_etime` varchar(255) not null default '23:59', + `enable_days_of_week` varchar(255) not null default '' comment 'split by space: 0 1 2 3 4 5 6', `enable_in_bg` tinyint(1) not null default 0 comment '1: only this bg 0: global', `notify_recovered` tinyint(1) not null comment 'whether notify when recovery', `notify_channels` varchar(255) not null default '' comment 'split by space: sms voice email dingtalk wecom', @@ -259,6 +278,7 @@ CREATE TABLE `alert_rule` ( `callbacks` varchar(255) not null default '' comment 'split by space: http://a.com/api/x http://a.com/api/y', `runbook_url` varchar(255), `append_tags` varchar(255) not null default '' comment 'split by space: service=n9e mod=api', + `annotations` text not null comment 'annotations', `create_at` bigint not null default 0, `create_by` varchar(64) not null default '', `update_at` bigint not null default 0, @@ -268,6 +288,10 @@ CREATE TABLE `alert_rule` ( KEY (`update_at`) ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; +alter table alert_rule add datasource_ids varchar(255) not null default ''; +alter table alert_rule add rule_config text not null comment 'rule_config'; +alter table alert_rule add annotations text not null comment 'annotations'; + CREATE TABLE `alert_mute` ( `id` bigint unsigned not null auto_increment, `group_id` bigint not null default 0 comment 'busi group id', @@ -275,11 +299,14 @@ CREATE TABLE `alert_mute` ( `note` varchar(1024) not null default '', `cate` varchar(128) not null, `cluster` varchar(128) not null, + `datasource_ids` varchar(255) not null default '' comment 'datasource ids', `tags` varchar(4096) not null default '' comment 'json,map,tagkey->regexp|value', `cause` varchar(255) not null default '', `btime` bigint not null default 0 comment 'begin time', `etime` bigint not null default 0 comment 'end time', `disabled` tinyint(1) not null default 0 comment '0:enabled 1:disabled', + `mute_time_type` tinyint(1) not null default 0, + `periodic_mutes` varchar(4096) not null default '', `create_at` bigint not null default 0, `create_by` varchar(64) not null default '', `update_at` bigint not null default 0, @@ -289,12 +316,18 @@ CREATE TABLE `alert_mute` ( KEY (`group_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +alter table alert_mute add datasource_ids varchar(255) not null default ''; +alter table alert_mute add periodic_mutes varchar(4096) not null default ''; +alter table alert_mute add mute_time_type tinyint(1) not null default 0; + CREATE TABLE `alert_subscribe` ( `id` bigint unsigned not null auto_increment, `name` varchar(255) not null default '', `disabled` tinyint(1) not null default 0 comment '0:enabled 1:disabled', `group_id` bigint not null default 0 comment 'busi group id', + `prod` varchar(255) not null default '', `cate` varchar(128) not null, + `datasource_ids` varchar(255) not null default '' comment 'datasource ids', `cluster` varchar(128) not null, `rule_id` bigint not null default 0, `tags` varchar(4096) not null default '' comment 'json,map,tagkey->regexp|value', @@ -303,6 +336,9 @@ CREATE TABLE `alert_subscribe` ( `redefine_channels` tinyint(1) default 0 comment 'is redefine channels?', `new_channels` varchar(255) not null default '' comment 'split by space: sms voice email dingtalk wecom', `user_group_ids` varchar(250) not null comment 'split by space 1 34 5, notify cc to user_group_ids', + `webhooks` text not null default '', + `redefine_webhooks` tinyint(1) default 0, + `for_duration` bigint not null default 0 `create_at` bigint not null default 0, `create_by` varchar(64) not null default '', `update_at` bigint not null default 0, @@ -312,19 +348,35 @@ CREATE TABLE `alert_subscribe` ( KEY (`group_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +alter table `alert_subscribe` add datasource_ids varchar(255) not null default ''; +alter table `alert_subscribe` add prod varchar(255) not null default ''; +alter table `alert_subscribe` add webhooks text not null default ''; +alter table `alert_subscribe` add redefine_webhooks tinyint(1) default 0; +alter table `alert_subscribe` add for_duration bigint not null default 0; + CREATE TABLE `target` ( `id` bigint unsigned not null auto_increment, `group_id` bigint not null default 0 comment 'busi group id', - `cluster` varchar(128) not null comment 'append to alert event as field', + `datasource_id` bigint not null default 0 comment 'datasource id', + `cluster` varchar(128) not null default '', `ident` varchar(191) not null comment 'target id', `note` varchar(255) not null default '' comment 'append to alert event as field', `tags` varchar(512) not null default '' comment 'append to series data as tags, split by space, append external space at suffix', `update_at` bigint not null default 0, + `offset` bigint not null default 0, PRIMARY KEY (`id`), UNIQUE KEY (`ident`), - KEY (`group_id`) + KEY (`group_id`), + KEY (`datasource_id`), + KEY (`update_at`), + KEY (`offset`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +alter table `target` add datasource_id bigint unsigned not null default 0; +alter table `target` add offset bigint not null default 0; +alter table `target` modify cluster varchar(128) not null default ''; + + -- case1: target_idents; case2: target_tags -- CREATE TABLE `collect_rule` ( -- `id` bigint unsigned not null auto_increment, @@ -359,10 +411,11 @@ CREATE TABLE `metric_view` ( ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; insert into metric_view(name, cate, configs) values('Host View', 0, '{"filters":[{"oper":"=","label":"__name__","value":"cpu_usage_idle"}],"dynamicLabels":[],"dimensionLabels":[{"label":"ident","value":""}]}'); - + CREATE TABLE `recording_rule` ( `id` bigint unsigned not null auto_increment, `group_id` bigint not null default '0' comment 'group_id', + `datasource_id` bigint not null default 0 comment 'datasource id', `cluster` varchar(128) not null, `name` varchar(255) not null comment 'new metric name', `note` varchar(255) not null comment 'rule note', @@ -379,6 +432,8 @@ CREATE TABLE `recording_rule` ( KEY `update_at` (`update_at`) ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; +alter table recording_rule add datasource_ids varchar(255) default ''; + CREATE TABLE `alert_aggr_view` ( `id` bigint unsigned not null auto_increment, `name` varchar(191) not null default '', @@ -397,6 +452,7 @@ insert into alert_aggr_view(name, rule, cate) values('By RuleName', 'field:rule_ CREATE TABLE `alert_cur_event` ( `id` bigint unsigned not null comment 'use alert_his_event.id', `cate` varchar(128) not null, + `datasource_id` bigint not null default 0 comment 'datasource id', `cluster` varchar(128) not null, `group_id` bigint unsigned not null comment 'busi group id of rule', `group_name` varchar(255) not null default '' comment 'busi group name', @@ -422,6 +478,8 @@ CREATE TABLE `alert_cur_event` ( `first_trigger_time` bigint, `trigger_time` bigint not null, `trigger_value` varchar(255) not null, + `annotations` text not null comment 'annotations', + `rule_config` text not null comment 'annotations', `tags` varchar(1024) not null default '' comment 'merge data_tags rule_tags, split by ,,', PRIMARY KEY (`id`), KEY (`hash`), @@ -430,10 +488,15 @@ CREATE TABLE `alert_cur_event` ( KEY (`notify_repeat_next`) ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; +alter table alert_cur_event add datasource_id bigint unsigned not null default 0; +alter table alert_cur_event add annotations text not null comment 'annotations'; +alter table alert_cur_event add rule_config text not null comment 'rule_config'; + CREATE TABLE `alert_his_event` ( `id` bigint unsigned not null AUTO_INCREMENT, `is_recovered` tinyint(1) not null, `cate` varchar(128) not null, + `datasource_id` bigint not null default 0 comment 'datasource id', `cluster` varchar(128) not null, `group_id` bigint unsigned not null comment 'busi group id of rule', `group_name` varchar(255) not null default '' comment 'busi group name', @@ -461,12 +524,19 @@ CREATE TABLE `alert_his_event` ( `recover_time` bigint not null default 0, `last_eval_time` bigint not null default 0 comment 'for time filter', `tags` varchar(1024) not null default '' comment 'merge data_tags rule_tags, split by ,,', + `annotations` text not null comment 'annotations', + `rule_config` text not null comment 'annotations', PRIMARY KEY (`id`), KEY (`hash`), KEY (`rule_id`), KEY (`trigger_time`, `group_id`) ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; +alter table alert_his_event add datasource_id bigint unsigned not null default 0; +alter table alert_his_event add annotations text not null comment 'annotations'; +alter table alert_his_event add rule_config text not null comment 'rule_config'; + + CREATE TABLE `task_tpl` ( `id` int unsigned NOT NULL AUTO_INCREMENT, @@ -523,7 +593,55 @@ CREATE TABLE `alerting_engines` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `instance` varchar(128) not null default '' comment 'instance identification, e.g. 10.9.0.9:9090', - `cluster` varchar(128) not null default '' comment 'target reader cluster', + `datasource_id` bigint not null default 0 comment 'datasource id', + `cluster` varchar(128) not null default '' comment 'n9e-alert cluster', `clock` bigint not null, PRIMARY KEY (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +alter table alerting_engines add datasource_id bigint unsigned not null default 0; + +CREATE TABLE `datasource` +( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) not null default '', + `description` varchar(255) not null default '', + `category` varchar(255) not null default '', + `plugin_id` int unsigned not null default 0, + `plugin_type` varchar(255) not null default '', + `plugin_type_name` varchar(255) not null default '', + `cluster_name` varchar(255) not null default '', + `settings` text not null, + `status` varchar(255) not null default '', + `http` varchar(4096) not null default '', + `auth` varchar(8192) not null default '', + `created_at` bigint not null default 0, + `created_by` varchar(64) not null default '', + `updated_at` bigint not null default 0, + `updated_by` varchar(64) not null default '', + PRIMARY KEY (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `builtin_cate` ( + `id` bigint unsigned not null auto_increment, + `name` varchar(191) not null, + `user_id` bigint not null default 0, + PRIMARY KEY (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `notify_tpl` ( + `id` bigint unsigned not null auto_increment, + `channel` varchar(32) not null, + `name` varchar(255) not null, + `content` text not null, + PRIMARY KEY (`id`), + UNIQUE KEY (`channel`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `sso_config` ( + `id` bigint unsigned not null auto_increment, + `name` varchar(255) not null, + `content` text not null, + PRIMARY KEY (`id`), + UNIQUE KEY (`name`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; \ No newline at end of file diff --git a/src/pkg/aop/logger.go b/pkg/aop/log.go similarity index 100% rename from src/pkg/aop/logger.go rename to pkg/aop/log.go diff --git a/src/pkg/aop/recovery.go b/pkg/aop/rec.go similarity index 100% rename from src/pkg/aop/recovery.go rename to pkg/aop/rec.go diff --git a/src/pkg/cas/cas.go b/pkg/cas/cas.go similarity index 56% rename from src/pkg/cas/cas.go rename to pkg/cas/cas.go index d97454219ca1813ee1d149a27b3e1bf7f56d1670..e01edab8aeb4fbc4e58e0cfccbd6cc4fe807b55e 100644 --- a/src/pkg/cas/cas.go +++ b/pkg/cas/cas.go @@ -5,9 +5,11 @@ import ( "context" "net/url" "strings" + "sync" "time" - "github.com/didi/nightingale/v5/src/storage" + "github.com/ccfos/nightingale/v6/storage" + "github.com/google/uuid" "github.com/toolkits/pkg/cas" "github.com/toolkits/pkg/logger" @@ -27,7 +29,8 @@ type Config struct { DefaultRoles []string } -type ssoClient struct { +type SsoClient struct { + enable bool config Config ssoAddr string callbackAddr string @@ -37,17 +40,16 @@ type ssoClient struct { phone string email string } -} -var ( - cli ssoClient -) + sync.RWMutex +} -func Init(cf Config) { +func New(cf Config) *SsoClient { + var cli SsoClient if !cf.Enable { - return + return &cli } - cli = ssoClient{} + cli.config = cf cli.ssoAddr = cf.SsoAddr cli.callbackAddr = cf.RedirectURL @@ -55,39 +57,67 @@ func Init(cf Config) { cli.attributes.nickname = cf.Attributes.Nickname cli.attributes.phone = cf.Attributes.Phone cli.attributes.email = cf.Attributes.Email + + return &cli +} + +func (s *SsoClient) Reload(cf Config) { + s.Lock() + defer s.Unlock() + if !cf.Enable { + s.enable = cf.Enable + return + } + + s.enable = cf.Enable + s.config = cf + s.ssoAddr = cf.SsoAddr + s.callbackAddr = cf.RedirectURL + s.displayName = cf.DisplayName + s.attributes.nickname = cf.Attributes.Nickname + s.attributes.phone = cf.Attributes.Phone + s.attributes.email = cf.Attributes.Email } -func GetDisplayName() string { - return cli.displayName +func (s *SsoClient) GetDisplayName() string { + s.RLock() + defer s.RUnlock() + if !s.enable { + return "" + } + + return s.displayName } // Authorize return the cas authorize location and state -func Authorize(redirect string) (string, string, error) { +func (s *SsoClient) Authorize(redis storage.Redis, redirect string) (string, string, error) { state := uuid.New().String() ctx := context.Background() - err := storage.Redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err() + err := redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err() if err != nil { return "", "", err } - return cli.genRedirectURL(state), state, nil + return s.genRedirectURL(state), state, nil } -func fetchRedirect(ctx context.Context, state string) (string, error) { - return storage.Redis.Get(ctx, wrapStateKey(state)).Result() +func fetchRedirect(ctx context.Context, state string, redis storage.Redis) (string, error) { + return redis.Get(ctx, wrapStateKey(state)).Result() } -func deleteRedirect(ctx context.Context, state string) error { - return storage.Redis.Del(ctx, wrapStateKey(state)).Err() +func deleteRedirect(ctx context.Context, state string, redis storage.Redis) error { + return redis.Del(ctx, wrapStateKey(state)).Err() } func wrapStateKey(key string) string { return "n9e_cas_" + key } -func (cli *ssoClient) genRedirectURL(state string) string { +func (s *SsoClient) genRedirectURL(state string) string { var buf bytes.Buffer + s.RLock() + defer s.RUnlock() - ssoAddr, err := url.Parse(cli.config.SsoAddr) + ssoAddr, err := url.Parse(s.config.SsoAddr) ssoAddr.Path = "login" if err != nil { logger.Error(err) @@ -96,9 +126,9 @@ func (cli *ssoClient) genRedirectURL(state string) string { buf.WriteString(ssoAddr.String()) v := url.Values{ - "service": {cli.callbackAddr}, + "service": {s.callbackAddr}, } - if strings.Contains(cli.ssoAddr, "?") { + if strings.Contains(s.ssoAddr, "?") { buf.WriteByte('&') } else { buf.WriteByte('?') @@ -117,13 +147,16 @@ type CallbackOutput struct { Email string `yaml:"email"` } -func ValidateServiceTicket(ctx context.Context, ticket, state string) (ret *CallbackOutput, err error) { - casUrl, err := url.Parse(cli.config.SsoAddr) +func (s *SsoClient) ValidateServiceTicket(ctx context.Context, ticket, state string, redis storage.Redis) (ret *CallbackOutput, err error) { + s.RLock() + defer s.RUnlock() + + casUrl, err := url.Parse(s.config.SsoAddr) if err != nil { logger.Error(err) return } - serviceUrl, err := url.Parse(cli.callbackAddr) + serviceUrl, err := url.Parse(s.callbackAddr) if err != nil { logger.Error(err) return @@ -140,17 +173,17 @@ func ValidateServiceTicket(ctx context.Context, ticket, state string) (ret *Call } ret = &CallbackOutput{} ret.Username = authRet.User - ret.Nickname = authRet.Attributes.Get(cli.attributes.nickname) + ret.Nickname = authRet.Attributes.Get(s.attributes.nickname) logger.Debugf("CAS Authentication Response's Attributes--[Nickname]: %s", ret.Nickname) - ret.Email = authRet.Attributes.Get(cli.attributes.email) + ret.Email = authRet.Attributes.Get(s.attributes.email) logger.Debugf("CAS Authentication Response's Attributes--[Email]: %s", ret.Email) - ret.Phone = authRet.Attributes.Get(cli.attributes.phone) + ret.Phone = authRet.Attributes.Get(s.attributes.phone) logger.Debugf("CAS Authentication Response's Attributes--[Phone]: %s", ret.Phone) - ret.Redirect, err = fetchRedirect(ctx, state) + ret.Redirect, err = fetchRedirect(ctx, state, redis) if err != nil { logger.Debugf("get redirect err:%s state:%s", state, err) } - err = deleteRedirect(ctx, state) + err = deleteRedirect(ctx, state, redis) if err != nil { logger.Debugf("delete redirect err:%s state:%s", state, err) } diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go new file mode 100644 index 0000000000000000000000000000000000000000..400516e94cdc0be1392adec7e803971f7403922b --- /dev/null +++ b/pkg/cfg/cfg.go @@ -0,0 +1,53 @@ +package cfg + +import ( + "bytes" + "fmt" + "path" + "strings" + + "github.com/koding/multiconfig" + "github.com/toolkits/pkg/file" +) + +func LoadConfigByDir(configDir string, configPtr interface{}) error { + var ( + tBuf []byte + ) + + loaders := []multiconfig.Loader{ + &multiconfig.TagLoader{}, + &multiconfig.EnvironmentLoader{}, + } + + files, err := file.FilesUnder(configDir) + if err != nil { + return fmt.Errorf("failed to list files under: %s : %v", configDir, err) + } + s := NewFileScanner() + for _, fpath := range files { + switch { + case strings.HasSuffix(fpath, ".toml"): + s.Read(path.Join(configDir, fpath)) + tBuf = append(tBuf, s.Data()...) + tBuf = append(tBuf, []byte("\n")...) + case strings.HasSuffix(fpath, ".json"): + loaders = append(loaders, &multiconfig.JSONLoader{Path: path.Join(configDir, fpath)}) + case strings.HasSuffix(fpath, ".yaml") || strings.HasSuffix(fpath, ".yml"): + loaders = append(loaders, &multiconfig.YAMLLoader{Path: path.Join(configDir, fpath)}) + } + if s.Err() != nil { + return s.Err() + } + } + + if len(tBuf) != 0 { + loaders = append(loaders, &multiconfig.TOMLLoader{Reader: bytes.NewReader(tBuf)}) + } + + m := multiconfig.DefaultLoader{ + Loader: multiconfig.MultiLoader(loaders...), + Validator: multiconfig.MultiValidator(&multiconfig.RequiredValidator{}), + } + return m.Load(configPtr) +} diff --git a/pkg/cfg/scan.go b/pkg/cfg/scan.go new file mode 100644 index 0000000000000000000000000000000000000000..75b240725ef3188f6966196d601486911f737ff3 --- /dev/null +++ b/pkg/cfg/scan.go @@ -0,0 +1,28 @@ +package cfg + +import ( + "io/ioutil" +) + +type scanner struct { + data []byte + err error +} + +func NewFileScanner() *scanner { + return &scanner{} +} + +func (s *scanner) Err() error { + return s.err +} + +func (s *scanner) Data() []byte { + return s.data +} + +func (s *scanner) Read(file string) { + if s.err == nil { + s.data, s.err = ioutil.ReadFile(file) + } +} diff --git a/pkg/choice/choice.go b/pkg/choice/choice.go new file mode 100644 index 0000000000000000000000000000000000000000..59cd5710153ac02f8379ea56dc1b4749b8bd0831 --- /dev/null +++ b/pkg/choice/choice.go @@ -0,0 +1,49 @@ +// Package choice provides basic functions for working with +// plugin options that must be one of several values. +package choice + +import ( + "fmt" + "strings" +) + +// Contains return true if the choice in the list of choices. +func Contains(choice string, choices []string) bool { + for _, item := range choices { + if item == choice { + return true + } + } + return false +} + +// Contains return true if the choice in the list of choices. +func ContainsPrefix(choice string, choices []string) bool { + for _, item := range choices { + if strings.HasPrefix(choice, item) { + return true + } + } + return false +} + +// Check returns an error if a choice is not one of +// the available choices. +func Check(choice string, available []string) error { + if !Contains(choice, available) { + return fmt.Errorf("unknown choice %s", choice) + } + return nil +} + +// CheckSlice returns an error if the choices is not a subset of +// available. +func CheckSlice(choices, available []string) error { + for _, choice := range choices { + err := Check(choice, available) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/ctx/ctx.go b/pkg/ctx/ctx.go new file mode 100644 index 0000000000000000000000000000000000000000..b52cfac1ceed19e9b5b7139d51adb541888245b7 --- /dev/null +++ b/pkg/ctx/ctx.go @@ -0,0 +1,34 @@ +package ctx + +import ( + "context" + + "gorm.io/gorm" +) + +type Context struct { + DB *gorm.DB + Ctx context.Context +} + +func NewContext(ctx context.Context, db *gorm.DB) *Context { + return &Context{ + Ctx: ctx, + DB: db, + } +} + +// set db to Context +func (c *Context) SetDB(db *gorm.DB) { + c.DB = db +} + +// get context from Context +func (c *Context) GetContext() context.Context { + return c.Ctx +} + +// get db from Context +func (c *Context) GetDB() *gorm.DB { + return c.DB +} diff --git a/src/pkg/httpx/httpx.go b/pkg/httpx/httpx.go similarity index 55% rename from src/pkg/httpx/httpx.go rename to pkg/httpx/httpx.go index 0baa31dc0513c0db539f1613c3178e2e6016a960..d5b71425041288e92cef1f99e27ea10721f2376c 100644 --- a/src/pkg/httpx/httpx.go +++ b/pkg/httpx/httpx.go @@ -5,7 +5,15 @@ import ( "crypto/tls" "fmt" "net/http" + "os" + "strings" "time" + + "github.com/ccfos/nightingale/v6/pkg/aop" + "github.com/ccfos/nightingale/v6/pkg/version" + "github.com/gin-contrib/pprof" + "github.com/gin-gonic/gin" + "github.com/prometheus/client_golang/prometheus/promhttp" ) type Config struct { @@ -15,11 +23,63 @@ type Config struct { KeyFile string PProf bool PrintAccessLog bool + ExposeMetrics bool ShutdownTimeout int MaxContentLength int64 ReadTimeout int WriteTimeout int IdleTimeout int + BasicAuth gin.Accounts +} + +func GinEngine(mode string, cfg Config) *gin.Engine { + gin.SetMode(mode) + + loggerMid := aop.Logger() + recoveryMid := aop.Recovery() + + if strings.ToLower(mode) == "release" { + aop.DisableConsoleColor() + } + + r := gin.New() + + r.Use(recoveryMid) + + // whether print access log + if cfg.PrintAccessLog { + r.Use(loggerMid) + } + + if cfg.PProf { + pprof.Register(r, "/api/debug/pprof") + } + + r.GET("/ping", func(c *gin.Context) { + c.String(200, "pong") + }) + + r.GET("/pid", func(c *gin.Context) { + c.String(200, fmt.Sprintf("%d", os.Getpid())) + }) + + r.GET("/ppid", func(c *gin.Context) { + c.String(200, fmt.Sprintf("%d", os.Getppid())) + }) + + r.GET("/addr", func(c *gin.Context) { + c.String(200, c.Request.RemoteAddr) + }) + + r.GET("/api/n9e/version", func(c *gin.Context) { + c.String(200, version.Version) + }) + + if cfg.ExposeMetrics { + r.GET("/metrics", gin.WrapH(promhttp.Handler())) + } + + return r } func Init(cfg Config, handler http.Handler) func() { diff --git a/src/pkg/ibex/ibex.go b/pkg/ibex/ibex.go similarity index 100% rename from src/pkg/ibex/ibex.go rename to pkg/ibex/ibex.go diff --git a/src/pkg/ldapx/ldapx.go b/pkg/ldapx/ldapx.go similarity index 62% rename from src/pkg/ldapx/ldapx.go rename to pkg/ldapx/ldapx.go index a5366460060a72f92074150bef1a3a56898357f8..24978a2f4617c7fed565f1e21493c4404426cbce 100644 --- a/src/pkg/ldapx/ldapx.go +++ b/pkg/ldapx/ldapx.go @@ -3,11 +3,12 @@ package ldapx import ( "crypto/tls" "fmt" + "sync" "github.com/go-ldap/ldap/v3" ) -type LdapSection struct { +type Config struct { Enable bool Host string Port int @@ -22,21 +23,67 @@ type LdapSection struct { DefaultRoles []string } +type SsoClient struct { + Enable bool + Host string + Port int + BaseDn string + BindUser string + BindPass string + AuthFilter string + Attributes LdapAttributes + CoverAttributes bool + TLS bool + StartTLS bool + DefaultRoles []string + + sync.RWMutex +} + type LdapAttributes struct { Nickname string `yaml:"nickname"` Phone string `yaml:"phone"` Email string `yaml:"email"` } -var LDAP LdapSection +func New(cf Config) *SsoClient { + var s = &SsoClient{} + if !cf.Enable { + return s + } + s.Reload(cf) + return s +} -func Init(ldap LdapSection) { - LDAP = ldap +func (s *SsoClient) Reload(cf Config) { + s.Lock() + defer s.Unlock() + if !cf.Enable { + s.Enable = cf.Enable + return + } + + s.Enable = cf.Enable + s.Host = cf.Host + s.Port = cf.Port + s.BaseDn = cf.BaseDn + s.BindUser = cf.BindUser + s.BindPass = cf.BindPass + s.AuthFilter = cf.AuthFilter + s.Attributes = cf.Attributes + s.CoverAttributes = cf.CoverAttributes + s.TLS = cf.TLS + s.StartTLS = cf.StartTLS + s.DefaultRoles = cf.DefaultRoles } -func genLdapAttributeSearchList() []string { +func (s *SsoClient) genLdapAttributeSearchList() []string { var ldapAttributes []string - attrs := LDAP.Attributes + + s.RLock() + attrs := s.Attributes + s.RUnlock() + if attrs.Nickname != "" { ldapAttributes = append(ldapAttributes, attrs.Nickname) } @@ -49,10 +96,14 @@ func genLdapAttributeSearchList() []string { return ldapAttributes } -func LdapReq(user, pass string) (*ldap.SearchResult, error) { +func (s *SsoClient) LdapReq(user, pass string) (*ldap.SearchResult, error) { var conn *ldap.Conn var err error - lc := LDAP + + s.RLock() + lc := s + s.RUnlock() + addr := fmt.Sprintf("%s:%d", lc.Host, lc.Port) if lc.TLS { @@ -85,7 +136,7 @@ func LdapReq(user, pass string) (*ldap.SearchResult, error) { lc.BaseDn, // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, fmt.Sprintf(lc.AuthFilter, user), // The filter to apply - genLdapAttributeSearchList(), // A list attributes to retrieve + s.genLdapAttributeSearchList(), // A list attributes to retrieve nil, ) @@ -95,7 +146,7 @@ func LdapReq(user, pass string) (*ldap.SearchResult, error) { } if len(sr.Entries) == 0 { - return nil, fmt.Errorf("Username or password invalid") + return nil, fmt.Errorf("username or password invalid") } if len(sr.Entries) > 1 { @@ -103,7 +154,7 @@ func LdapReq(user, pass string) (*ldap.SearchResult, error) { } if err := conn.Bind(sr.Entries[0].DN, pass); err != nil { - return nil, fmt.Errorf("Username or password invalid") + return nil, fmt.Errorf("username or password invalid") } return sr, nil diff --git a/src/pkg/logx/logx.go b/pkg/logx/logx.go similarity index 100% rename from src/pkg/logx/logx.go rename to pkg/logx/logx.go diff --git a/src/pkg/oauth2x/oauth2x.go b/pkg/oauth2x/oauth2x.go similarity index 61% rename from src/pkg/oauth2x/oauth2x.go rename to pkg/oauth2x/oauth2x.go index 58356d9a61283704fe7292875e2c944012fad42b..9c9d32fc04d763ecedeeaa7fbb8de7ca738eb8cc 100644 --- a/src/pkg/oauth2x/oauth2x.go +++ b/pkg/oauth2x/oauth2x.go @@ -6,9 +6,11 @@ import ( "fmt" "io/ioutil" "net/http" + "sync" "time" - "github.com/didi/nightingale/v5/src/storage" + "github.com/ccfos/nightingale/v6/storage" + "github.com/toolkits/pkg/logger" "github.com/google/uuid" @@ -16,7 +18,8 @@ import ( "golang.org/x/oauth2" ) -type ssoClient struct { +type SsoClient struct { + enable bool config oauth2.Config ssoAddr string userInfoAddr string @@ -32,6 +35,8 @@ type ssoClient struct { } userinfoIsArray bool userinfoPrefix string + + sync.RWMutex } type Config struct { @@ -57,29 +62,38 @@ type Config struct { Scopes []string } -var ( - cli ssoClient -) +func New(cf Config) *SsoClient { + var s = &SsoClient{} + if !cf.Enable { + return s + } + s.Reload(cf) + return s +} -func Init(cf Config) { +func (s *SsoClient) Reload(cf Config) { + s.Lock() + defer s.Unlock() if !cf.Enable { + s.enable = cf.Enable return } - cli.ssoAddr = cf.SsoAddr - cli.userInfoAddr = cf.UserInfoAddr - cli.TranTokenMethod = cf.TranTokenMethod - cli.callbackAddr = cf.RedirectURL - cli.displayName = cf.DisplayName - cli.coverAttributes = cf.CoverAttributes - cli.attributes.username = cf.Attributes.Username - cli.attributes.nickname = cf.Attributes.Nickname - cli.attributes.phone = cf.Attributes.Phone - cli.attributes.email = cf.Attributes.Email - cli.userinfoIsArray = cf.UserinfoIsArray - cli.userinfoPrefix = cf.UserinfoPrefix - - cli.config = oauth2.Config{ + s.enable = cf.Enable + s.ssoAddr = cf.SsoAddr + s.userInfoAddr = cf.UserInfoAddr + s.TranTokenMethod = cf.TranTokenMethod + s.callbackAddr = cf.RedirectURL + s.displayName = cf.DisplayName + s.coverAttributes = cf.CoverAttributes + s.attributes.username = cf.Attributes.Username + s.attributes.nickname = cf.Attributes.Nickname + s.attributes.phone = cf.Attributes.Phone + s.attributes.email = cf.Attributes.Email + s.userinfoIsArray = cf.UserinfoIsArray + s.userinfoPrefix = cf.UserinfoPrefix + + s.config = oauth2.Config{ ClientID: cf.ClientId, ClientSecret: cf.ClientSecret, Endpoint: oauth2.Endpoint{ @@ -91,8 +105,14 @@ func Init(cf Config) { } } -func GetDisplayName() string { - return cli.displayName +func (s *SsoClient) GetDisplayName() string { + s.RLock() + defer s.RUnlock() + if !s.enable { + return "" + } + + return s.displayName } func wrapStateKey(key string) string { @@ -100,38 +120,40 @@ func wrapStateKey(key string) string { } // Authorize return the sso authorize location with state -func Authorize(redirect string) (string, error) { +func (s *SsoClient) Authorize(redis storage.Redis, redirect string) (string, error) { state := uuid.New().String() ctx := context.Background() - err := storage.Redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err() + err := redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err() if err != nil { return "", err } - return cli.config.AuthCodeURL(state), nil + s.RLock() + defer s.RUnlock() + return s.config.AuthCodeURL(state), nil } -func fetchRedirect(ctx context.Context, state string) (string, error) { - return storage.Redis.Get(ctx, wrapStateKey(state)).Result() +func fetchRedirect(redis storage.Redis, ctx context.Context, state string) (string, error) { + return redis.Get(ctx, wrapStateKey(state)).Result() } -func deleteRedirect(ctx context.Context, state string) error { - return storage.Redis.Del(ctx, wrapStateKey(state)).Err() +func deleteRedirect(redis storage.Redis, ctx context.Context, state string) error { + return redis.Del(ctx, wrapStateKey(state)).Err() } // Callback 用 code 兑换 accessToken 以及 用户信息 -func Callback(ctx context.Context, code, state string) (*CallbackOutput, error) { - ret, err := exchangeUser(code) +func (s *SsoClient) Callback(redis storage.Redis, ctx context.Context, code, state string) (*CallbackOutput, error) { + ret, err := s.exchangeUser(code) if err != nil { return nil, fmt.Errorf("ilegal user:%v", err) } - ret.Redirect, err = fetchRedirect(ctx, state) + ret.Redirect, err = fetchRedirect(redis, ctx, state) if err != nil { logger.Errorf("get redirect err:%v code:%s state:%s", code, state, err) } - err = deleteRedirect(ctx, state) + err = deleteRedirect(redis, ctx, state) if err != nil { logger.Errorf("delete redirect err:%v code:%s state:%s", code, state, err) } @@ -148,14 +170,17 @@ type CallbackOutput struct { Email string `yaml:"email"` } -func exchangeUser(code string) (*CallbackOutput, error) { +func (s *SsoClient) exchangeUser(code string) (*CallbackOutput, error) { + s.RLock() + defer s.RUnlock() + ctx := context.Background() - oauth2Token, err := cli.config.Exchange(ctx, code) + oauth2Token, err := s.config.Exchange(ctx, code) if err != nil { return nil, fmt.Errorf("failed to exchange token: %s", err) } - userInfo, err := getUserInfo(cli.userInfoAddr, oauth2Token.AccessToken, cli.TranTokenMethod) + userInfo, err := getUserInfo(s.userInfoAddr, oauth2Token.AccessToken, s.TranTokenMethod) if err != nil { logger.Errorf("failed to get user info: %s", err) return nil, fmt.Errorf("failed to get user info: %s", err) @@ -163,10 +188,10 @@ func exchangeUser(code string) (*CallbackOutput, error) { return &CallbackOutput{ AccessToken: oauth2Token.AccessToken, - Username: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.username), - Nickname: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.nickname), - Phone: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.phone), - Email: getUserinfoField(userInfo, cli.userinfoIsArray, cli.userinfoPrefix, cli.attributes.email), + Username: getUserinfoField(userInfo, s.userinfoIsArray, s.userinfoPrefix, s.attributes.username), + Nickname: getUserinfoField(userInfo, s.userinfoIsArray, s.userinfoPrefix, s.attributes.nickname), + Phone: getUserinfoField(userInfo, s.userinfoIsArray, s.userinfoPrefix, s.attributes.phone), + Email: getUserinfoField(userInfo, s.userinfoIsArray, s.userinfoPrefix, s.attributes.email), }, nil } diff --git a/src/pkg/oidcc/oidc.go b/pkg/oidcx/oidc.go similarity index 51% rename from src/pkg/oidcc/oidc.go rename to pkg/oidcx/oidc.go index 1ec472ee628623e2012ad9b2ca14ac582f6373ae..5416ef784fdb18a0ff34dd389009d7e69fc3e65b 100644 --- a/src/pkg/oidcc/oidc.go +++ b/pkg/oidcx/oidc.go @@ -1,12 +1,12 @@ -package oidcc +package oidcx import ( "context" "fmt" - "log" + "sync" "time" - "github.com/didi/nightingale/v5/src/storage" + "github.com/ccfos/nightingale/v6/storage" oidc "github.com/coreos/go-oidc" "github.com/google/uuid" @@ -14,7 +14,8 @@ import ( "golang.org/x/oauth2" ) -type ssoClient struct { +type SsoClient struct { + enable bool verifier *oidc.IDTokenVerifier config oauth2.Config ssoAddr string @@ -27,6 +28,8 @@ type ssoClient struct { phone string email string } + + sync.RWMutex } type Config struct { @@ -45,43 +48,59 @@ type Config struct { DefaultRoles []string } -var ( - cli ssoClient -) +func New(cf Config) (*SsoClient, error) { + var s = &SsoClient{} + if !cf.Enable { + return s, nil + } + err := s.Reload(cf) + return s, err +} -func Init(cf Config) { +func (s *SsoClient) Reload(cf Config) error { + s.Lock() + defer s.Unlock() if !cf.Enable { - return + s.enable = cf.Enable + return nil } - cli.ssoAddr = cf.SsoAddr - cli.callbackAddr = cf.RedirectURL - cli.coverAttributes = cf.CoverAttributes - cli.attributes.username = "sub" - cli.attributes.nickname = cf.Attributes.Nickname - cli.attributes.phone = cf.Attributes.Phone - cli.attributes.email = cf.Attributes.Email - cli.displayName = cf.DisplayName + s.enable = cf.Enable + s.ssoAddr = cf.SsoAddr + s.callbackAddr = cf.RedirectURL + s.coverAttributes = cf.CoverAttributes + s.attributes.username = "sub" + s.attributes.nickname = cf.Attributes.Nickname + s.attributes.phone = cf.Attributes.Phone + s.attributes.email = cf.Attributes.Email + s.displayName = cf.DisplayName provider, err := oidc.NewProvider(context.Background(), cf.SsoAddr) if err != nil { - log.Fatal(err) + return err } oidcConfig := &oidc.Config{ ClientID: cf.ClientId, } - cli.verifier = provider.Verifier(oidcConfig) - cli.config = oauth2.Config{ + s.verifier = provider.Verifier(oidcConfig) + s.config = oauth2.Config{ ClientID: cf.ClientId, ClientSecret: cf.ClientSecret, Endpoint: provider.Endpoint(), RedirectURL: cf.RedirectURL, Scopes: []string{oidc.ScopeOpenID, "profile", "email", "phone"}, } + return nil } -func GetDisplayName() string { - return cli.displayName +func (s *SsoClient) GetDisplayName() string { + s.RLock() + defer s.RUnlock() + if !s.enable { + return "" + } + + return s.displayName } func wrapStateKey(key string) string { @@ -89,39 +108,42 @@ func wrapStateKey(key string) string { } // Authorize return the sso authorize location with state -func Authorize(redirect string) (string, error) { +func (s *SsoClient) Authorize(redis storage.Redis, redirect string) (string, error) { + s.RLock() + defer s.RUnlock() + state := uuid.New().String() ctx := context.Background() - err := storage.Redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err() + err := redis.Set(ctx, wrapStateKey(state), redirect, time.Duration(300*time.Second)).Err() if err != nil { return "", err } - return cli.config.AuthCodeURL(state), nil + return s.config.AuthCodeURL(state), nil } -func fetchRedirect(ctx context.Context, state string) (string, error) { - return storage.Redis.Get(ctx, wrapStateKey(state)).Result() +func fetchRedirect(redis storage.Redis, ctx context.Context, state string) (string, error) { + return redis.Get(ctx, wrapStateKey(state)).Result() } -func deleteRedirect(ctx context.Context, state string) error { - return storage.Redis.Del(ctx, wrapStateKey(state)).Err() +func deleteRedirect(redis storage.Redis, ctx context.Context, state string) error { + return redis.Del(ctx, wrapStateKey(state)).Err() } // Callback 用 code 兑换 accessToken 以及 用户信息, -func Callback(ctx context.Context, code, state string) (*CallbackOutput, error) { - ret, err := exchangeUser(code) +func (s *SsoClient) Callback(redis storage.Redis, ctx context.Context, code, state string) (*CallbackOutput, error) { + ret, err := s.exchangeUser(code) if err != nil { return nil, fmt.Errorf("ilegal user:%v", err) } - ret.Redirect, err = fetchRedirect(ctx, state) + ret.Redirect, err = fetchRedirect(redis, ctx, state) if err != nil { logger.Debugf("get redirect err:%v code:%s state:%s", code, state, err) } - err = deleteRedirect(ctx, state) + err = deleteRedirect(redis, ctx, state) if err != nil { logger.Debugf("delete redirect err:%v code:%s state:%s", code, state, err) } @@ -138,20 +160,24 @@ type CallbackOutput struct { Email string `yaml:"email"` } -func exchangeUser(code string) (*CallbackOutput, error) { +func (s *SsoClient) exchangeUser(code string) (*CallbackOutput, error) { + s.RLock() + defer s.RUnlock() + ctx := context.Background() - oauth2Token, err := cli.config.Exchange(ctx, code) + oauth2Token, err := s.config.Exchange(ctx, code) if err != nil { - return nil, fmt.Errorf("Failed to exchange token: %s", err) + return nil, fmt.Errorf("failed to exchange token: %v", err) } rawIDToken, ok := oauth2Token.Extra("id_token").(string) if !ok { - return nil, fmt.Errorf("No id_token field in oauth2 token.") + return nil, fmt.Errorf("no id_token field in oauth2 token. ") } - idToken, err := cli.verifier.Verify(ctx, rawIDToken) + + idToken, err := s.verifier.Verify(ctx, rawIDToken) if err != nil { - return nil, fmt.Errorf("Failed to verify ID Token: %s", err) + return nil, fmt.Errorf("failed to verify ID Token: %v", err) } data := map[string]interface{}{} @@ -169,9 +195,9 @@ func exchangeUser(code string) (*CallbackOutput, error) { return &CallbackOutput{ AccessToken: oauth2Token.AccessToken, - Username: v(cli.attributes.username), - Nickname: v(cli.attributes.nickname), - Phone: v(cli.attributes.phone), - Email: v(cli.attributes.email), + Username: v(s.attributes.username), + Nickname: v(s.attributes.nickname), + Phone: v(s.attributes.phone), + Email: v(s.attributes.email), }, nil } diff --git a/src/pkg/ormx/ormx.go b/pkg/ormx/ormx.go similarity index 100% rename from src/pkg/ormx/ormx.go rename to pkg/ormx/ormx.go diff --git a/src/pkg/ormx/types.go b/pkg/ormx/types.go similarity index 100% rename from src/pkg/ormx/types.go rename to pkg/ormx/types.go diff --git a/pkg/osx/osx.go b/pkg/osx/osx.go new file mode 100644 index 0000000000000000000000000000000000000000..ac929a68cfab8c5bb3fa6006c4d445527e001ba9 --- /dev/null +++ b/pkg/osx/osx.go @@ -0,0 +1,11 @@ +package osx + +import "os" + +// getEnv returns the value of an environment variable, or returns the provided fallback value +func GetEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} diff --git a/src/pkg/poster/post.go b/pkg/poster/post.go similarity index 100% rename from src/pkg/poster/post.go rename to pkg/poster/post.go diff --git a/src/pkg/prom/client_option.go b/pkg/prom/client_option.go similarity index 83% rename from src/pkg/prom/client_option.go rename to pkg/prom/client_option.go index 1384f605de5ee76bd6cd77b20dd8ba8fc171efe1..b758a9d9756674bf45576eff72d811dc3e2e71fa 100644 --- a/src/pkg/prom/client_option.go +++ b/pkg/prom/client_option.go @@ -1,6 +1,7 @@ package prom type ClientOptions struct { + Url string BasicAuthUser string BasicAuthPass string Headers []string diff --git a/src/pkg/prom/api.go b/pkg/prom/reader.go similarity index 98% rename from src/pkg/prom/api.go rename to pkg/prom/reader.go index 5c6a3c2eb0f0b1005a3afcab0a774c7067f3c168..0912e5c868b941c020371a9efea7be284adcd2bc 100644 --- a/src/pkg/prom/api.go +++ b/pkg/prom/reader.go @@ -327,18 +327,6 @@ type RuleGroup struct { Rules Rules `json:"rules"` } -// Recording and alerting rules are stored in the same slice to preserve the order -// that rules are returned in by the API. -// -// Rule types can be determined using a type switch: -// switch v := rule.(type) { -// case RecordingRule: -// fmt.Print("got a recording rule") -// case AlertingRule: -// fmt.Print("got a alerting rule") -// default: -// fmt.Printf("unknown rule type %s", v) -// } type Rules []interface{} // AlertingRule models a alerting rule. diff --git a/pkg/prom/writer.go b/pkg/prom/writer.go new file mode 100644 index 0000000000000000000000000000000000000000..07c273473e21208b4997916078789fb1fdeed2b8 --- /dev/null +++ b/pkg/prom/writer.go @@ -0,0 +1,94 @@ +package prom + +import ( + "bytes" + "context" + "fmt" + "net/http" + + "github.com/golang/protobuf/proto" + "github.com/golang/snappy" + "github.com/prometheus/client_golang/api" + "github.com/prometheus/prometheus/prompb" + "github.com/toolkits/pkg/logger" +) + +type WriterType struct { + Opts ClientOptions + Client api.Client +} + +func NewWriter(cli api.Client, opt ClientOptions) WriterType { + writer := WriterType{ + Opts: opt, + Client: cli, + } + return writer +} + +func (w WriterType) Write(items []*prompb.TimeSeries, headers ...map[string]string) { + if len(items) == 0 { + return + } + + req := &prompb.WriteRequest{ + Timeseries: items, + } + + data, err := proto.Marshal(req) + if err != nil { + logger.Warningf("marshal prom data to proto got error: %v, data: %+v", err, items) + return + } + + if err := w.Post(snappy.Encode(nil, data), headers...); err != nil { + logger.Warningf("post to %s got error: %v", w.Opts.Url, err) + logger.Debug("example timeseries:", items[0].String()) + } +} + +func (w WriterType) Post(req []byte, headers ...map[string]string) error { + httpReq, err := http.NewRequest("POST", w.Opts.Url, bytes.NewReader(req)) + if err != nil { + logger.Warningf("create remote write request got error: %s", err.Error()) + return err + } + + httpReq.Header.Add("Content-Encoding", "snappy") + httpReq.Header.Set("Content-Type", "application/x-protobuf") + httpReq.Header.Set("User-Agent", "n9e") + httpReq.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0") + + if len(headers) > 0 { + for k, v := range headers[0] { + httpReq.Header.Set(k, v) + } + } + + if w.Opts.BasicAuthUser != "" { + httpReq.SetBasicAuth(w.Opts.BasicAuthUser, w.Opts.BasicAuthPass) + } + + headerCount := len(w.Opts.Headers) + if headerCount > 0 && headerCount%2 == 0 { + for i := 0; i < len(w.Opts.Headers); i += 2 { + httpReq.Header.Add(w.Opts.Headers[i], w.Opts.Headers[i+1]) + if w.Opts.Headers[i] == "Host" { + httpReq.Host = w.Opts.Headers[i+1] + } + } + } + + resp, body, err := w.Client.Do(context.Background(), httpReq) + if err != nil { + logger.Warningf("push data with remote write:%s request got error: %v, response body: %s", w.Opts.Url, err, string(body)) + return err + } + + if resp.StatusCode >= 400 { + err = fmt.Errorf("push data with remote write:%s request got status code: %v, response body: %s", w.Opts.Url, resp.StatusCode, string(body)) + return err + } + + return nil +} diff --git a/src/pkg/secu/aes.go b/pkg/secu/aes.go similarity index 100% rename from src/pkg/secu/aes.go rename to pkg/secu/aes.go diff --git a/src/pkg/tls/common.go b/pkg/tlsx/common.go similarity index 72% rename from src/pkg/tls/common.go rename to pkg/tlsx/common.go index 1ceb20c3f4c46b4125116592955808cb6dc02ad1..ac096d50bdf26093c8d8ae1bc7d4578df0ef5239 100644 --- a/src/pkg/tls/common.go +++ b/pkg/tlsx/common.go @@ -1,6 +1,9 @@ -package tls +package tlsx -import "crypto/tls" +import ( + "crypto/tls" + "fmt" +) var tlsVersionMap = map[string]uint16{ "TLS10": tls.VersionTLS10, @@ -36,3 +39,28 @@ var tlsCipherMap = map[string]uint16{ "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, } + +// ParseCiphers returns a `[]uint16` by received `[]string` key that represents ciphers from crypto/tls. +// If some of ciphers in received list doesn't exists ParseCiphers returns nil with error +func ParseCiphers(ciphers []string) ([]uint16, error) { + suites := []uint16{} + + for _, cipher := range ciphers { + v, ok := tlsCipherMap[cipher] + if !ok { + return nil, fmt.Errorf("unsupported cipher %q", cipher) + } + suites = append(suites, v) + } + + return suites, nil +} + +// ParseTLSVersion returns a `uint16` by received version string key that represents tls version from crypto/tls. +// If version isn't supported ParseTLSVersion returns 0 with error +func ParseTLSVersion(version string) (uint16, error) { + if v, ok := tlsVersionMap[version]; ok { + return v, nil + } + return 0, fmt.Errorf("unsupported version %q", version) +} diff --git a/src/pkg/tls/config.go b/pkg/tlsx/config.go similarity index 83% rename from src/pkg/tls/config.go rename to pkg/tlsx/config.go index a73ba6e190e2a9df4e14fc4b07f7ac788b771461..d6168c43b10a839860d2d8f04d508272b260274a 100644 --- a/src/pkg/tls/config.go +++ b/pkg/tlsx/config.go @@ -1,4 +1,4 @@ -package tls +package tlsx import ( "crypto/tls" @@ -7,36 +7,41 @@ import ( "os" "strings" - "github.com/toolkits/pkg/slice" + "github.com/ccfos/nightingale/v6/pkg/choice" ) // ClientConfig represents the standard client TLS config. type ClientConfig struct { - TLSCA string `toml:"tls_ca"` - TLSCert string `toml:"tls_cert"` - TLSKey string `toml:"tls_key"` - TLSKeyPwd string `toml:"tls_key_pwd"` - InsecureSkipVerify bool `toml:"insecure_skip_verify"` - ServerName string `toml:"tls_server_name"` - TLSMinVersion string `toml:"tls_min_version"` - TLSMaxVersion string `toml:"tls_max_version"` + UseTLS bool + TLSCA string + TLSCert string + TLSKey string + TLSKeyPwd string + InsecureSkipVerify bool + ServerName string + TLSMinVersion string + TLSMaxVersion string } // ServerConfig represents the standard server TLS config. type ServerConfig struct { - TLSCert string `toml:"tls_cert"` - TLSKey string `toml:"tls_key"` - TLSKeyPwd string `toml:"tls_key_pwd"` - TLSAllowedCACerts []string `toml:"tls_allowed_cacerts"` - TLSCipherSuites []string `toml:"tls_cipher_suites"` - TLSMinVersion string `toml:"tls_min_version"` - TLSMaxVersion string `toml:"tls_max_version"` - TLSAllowedDNSNames []string `toml:"tls_allowed_dns_names"` + TLSCert string + TLSKey string + TLSKeyPwd string + TLSAllowedCACerts []string + TLSCipherSuites []string + TLSMinVersion string + TLSMaxVersion string + TLSAllowedDNSNames []string } // TLSConfig returns a tls.Config, may be nil without error if TLS is not // configured. func (c *ClientConfig) TLSConfig() (*tls.Config, error) { + if !c.UseTLS { + return nil, nil + } + tlsConfig := &tls.Config{ InsecureSkipVerify: c.InsecureSkipVerify, Renegotiation: tls.RenegotiateNever, @@ -187,7 +192,7 @@ func (c *ServerConfig) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [ } for _, name := range cert.DNSNames { - if slice.ContainsString(c.TLSAllowedDNSNames, name) { + if choice.Contains(name, c.TLSAllowedDNSNames) { return nil } } diff --git a/src/pkg/tplx/conv.go b/pkg/tplx/conv.go similarity index 100% rename from src/pkg/tplx/conv.go rename to pkg/tplx/conv.go diff --git a/src/pkg/tplx/common.go b/pkg/tplx/fns.go similarity index 100% rename from src/pkg/tplx/common.go rename to pkg/tplx/fns.go diff --git a/src/pkg/tplx/tplx.go b/pkg/tplx/tplx.go similarity index 100% rename from src/pkg/tplx/tplx.go rename to pkg/tplx/tplx.go diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000000000000000000000000000000000000..0db118bc4c4c08e6244294e0b3a2318c304372e2 --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,3 @@ +package version + +var Version = "unknown" diff --git a/prom/client.go b/prom/client.go new file mode 100644 index 0000000000000000000000000000000000000000..016616425d4923a3de6be78763679b7a191ebc44 --- /dev/null +++ b/prom/client.go @@ -0,0 +1,102 @@ +package prom + +import ( + "sync" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/prom" +) + +type PromClientMap struct { + sync.RWMutex + ctx *ctx.Context + heartbeat aconf.HeartbeatConfig + ReaderClients map[int64]prom.API + WriterClients map[int64]prom.WriterType +} + +func (pc *PromClientMap) Set(datasourceId int64, r prom.API, w prom.WriterType) { + if r == nil { + return + } + pc.Lock() + defer pc.Unlock() + pc.ReaderClients[datasourceId] = r + pc.WriterClients[datasourceId] = w +} + +func (pc *PromClientMap) GetDatasourceIds() []int64 { + pc.RLock() + defer pc.RUnlock() + var datasourceIds []int64 + for k := range pc.ReaderClients { + datasourceIds = append(datasourceIds, k) + } + + return datasourceIds +} + +func (pc *PromClientMap) GetCli(datasourceId int64) prom.API { + pc.RLock() + defer pc.RUnlock() + c := pc.ReaderClients[datasourceId] + return c +} + +func (pc *PromClientMap) GetWriterCli(datasourceId int64) prom.WriterType { + pc.RLock() + defer pc.RUnlock() + c := pc.WriterClients[datasourceId] + return c +} + +func (pc *PromClientMap) IsNil(datasourceId int64) bool { + pc.RLock() + defer pc.RUnlock() + + c, exists := pc.ReaderClients[datasourceId] + if !exists { + return true + } + + return c == nil +} + +// Hit 根据当前有效的 datasourceId 和规则的 datasourceId 配置计算有效的cluster列表 +func (pc *PromClientMap) Hit(datasourceIds []int64) []int64 { + pc.RLock() + defer pc.RUnlock() + dsIds := make([]int64, 0, len(pc.ReaderClients)) + if len(datasourceIds) == 1 && datasourceIds[0] == models.DatasourceIdAll { + for c := range pc.ReaderClients { + datasourceIds = append(datasourceIds, c) + } + return datasourceIds + } + + for dsId := range pc.ReaderClients { + for _, id := range datasourceIds { + if id == dsId { + dsIds = append(dsIds, id) + continue + } + } + } + return dsIds +} + +func (pc *PromClientMap) Reset() { + pc.Lock() + defer pc.Unlock() + + pc.ReaderClients = make(map[int64]prom.API) + pc.WriterClients = make(map[int64]prom.WriterType) +} + +func (pc *PromClientMap) Del(datasourceId int64) { + pc.Lock() + defer pc.Unlock() + delete(pc.ReaderClients, datasourceId) +} diff --git a/src/server/config/prom_option.go b/prom/option.go similarity index 67% rename from src/server/config/prom_option.go rename to prom/option.go index d485ed072d1d4bbe02e1a9486d14055014984717..4b9ed2f5fe2de40872713845a17a89ae675c469e 100644 --- a/src/server/config/prom_option.go +++ b/prom/option.go @@ -1,23 +1,17 @@ -package config +package prom -import ( - "sync" - - "github.com/didi/nightingale/v5/src/pkg/tls" -) +import "sync" type PromOption struct { ClusterName string Url string + WriteAddr string BasicAuthUser string BasicAuthPass string Timeout int64 DialTimeout int64 - UseTLS bool - tls.ClientConfig - MaxIdleConnsPerHost int Headers []string @@ -62,28 +56,28 @@ func (po *PromOption) Equal(target PromOption) bool { } type PromOptionsStruct struct { - Data map[string]PromOption + Data map[int64]PromOption sync.RWMutex } -func (pos *PromOptionsStruct) Set(clusterName string, po PromOption) { +func (pos *PromOptionsStruct) Set(datasourceId int64, po PromOption) { pos.Lock() - pos.Data[clusterName] = po + pos.Data[datasourceId] = po pos.Unlock() } -func (pos *PromOptionsStruct) Del(clusterName string) { +func (pos *PromOptionsStruct) Del(datasourceId int64) { pos.Lock() - delete(pos.Data, clusterName) + delete(pos.Data, datasourceId) pos.Unlock() } -func (pos *PromOptionsStruct) Get(clusterName string) (PromOption, bool) { +func (pos *PromOptionsStruct) Get(datasourceId int64) (PromOption, bool) { pos.RLock() defer pos.RUnlock() - ret, has := pos.Data[clusterName] + ret, has := pos.Data[datasourceId] return ret, has } // Data key is cluster name -var PromOptions = &PromOptionsStruct{Data: make(map[string]PromOption)} +var PromOptions = &PromOptionsStruct{Data: make(map[int64]PromOption)} diff --git a/prom/reader.go b/prom/reader.go new file mode 100644 index 0000000000000000000000000000000000000000..9cb86153f7c8613edccbb789f1fe772f108340c3 --- /dev/null +++ b/prom/reader.go @@ -0,0 +1,181 @@ +package prom + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "time" + + "github.com/ccfos/nightingale/v6/alert/aconf" + "github.com/ccfos/nightingale/v6/models" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/prom" + + "github.com/prometheus/client_golang/api" + "github.com/toolkits/pkg/logger" +) + +func NewPromClient(ctx *ctx.Context, heartbeat aconf.HeartbeatConfig) *PromClientMap { + pc := &PromClientMap{ + ReaderClients: make(map[int64]prom.API), + WriterClients: make(map[int64]prom.WriterType), + ctx: ctx, + heartbeat: heartbeat, + } + pc.InitReader() + return pc +} + +func (pc *PromClientMap) InitReader() error { + go func() { + for { + pc.loadFromDatabase() + time.Sleep(time.Second) + } + }() + return nil +} + +type PromSetting struct { + WriterAddr string `json:"write_addr"` +} + +func (pc *PromClientMap) loadFromDatabase() { + datasources, err := models.GetDatasourcesGetsBy(pc.ctx, models.PROMETHEUS, "", "", 1000, 0) + if err != nil { + logger.Errorf("failed to get datasources, error: %v", err) + return + } + newCluster := make(map[int64]struct{}) + for _, ds := range datasources { + dsId := ds.Id + var header []string + for k, v := range ds.HTTPJson.Headers { + header = append(header, k) + header = append(header, v) + } + + var promSetting PromSetting + if ds.Settings != "" { + err := json.Unmarshal([]byte(ds.Settings), &promSetting) + if err != nil { + logger.Errorf("failed to unmarshal prom settings, error: %v", err) + continue + } + } + + po := PromOption{ + ClusterName: ds.Name, + Url: ds.HTTPJson.Url, + WriteAddr: promSetting.WriterAddr, + BasicAuthUser: ds.AuthJson.BasicAuthUser, + BasicAuthPass: ds.AuthJson.BasicAuthPassword, + Timeout: ds.HTTPJson.Timeout, + DialTimeout: ds.HTTPJson.DialTimeout, + MaxIdleConnsPerHost: ds.HTTPJson.MaxIdleConnsPerHost, + Headers: header, + } + + newCluster[dsId] = struct{}{} + if pc.IsNil(dsId) { + // first time + if err = pc.setClientFromPromOption(dsId, po); err != nil { + logger.Errorf("failed to setClientFromPromOption: %v", err) + continue + } + + logger.Info("setClientFromPromOption success: ", dsId) + PromOptions.Set(dsId, po) + continue + } + + localPo, has := PromOptions.Get(dsId) + if !has || !localPo.Equal(po) { + if err = pc.setClientFromPromOption(dsId, po); err != nil { + logger.Errorf("failed to setClientFromPromOption: %v", err) + continue + } + + PromOptions.Set(dsId, po) + } + } + + // delete useless cluster + oldIds := pc.GetDatasourceIds() + for _, oldId := range oldIds { + if _, has := newCluster[oldId]; !has { + pc.Del(oldId) + PromOptions.Del(oldId) + logger.Info("delete cluster: ", oldId) + } + } +} + +func (pc *PromClientMap) newReaderClientFromPromOption(po PromOption) (api.Client, error) { + return api.NewClient(api.Config{ + Address: po.Url, + RoundTripper: &http.Transport{ + // TLSClientConfig: tlsConfig, + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: time.Duration(po.DialTimeout) * time.Millisecond, + }).DialContext, + ResponseHeaderTimeout: time.Duration(po.Timeout) * time.Millisecond, + MaxIdleConnsPerHost: po.MaxIdleConnsPerHost, + }, + }) +} + +func (pc *PromClientMap) newWriterClientFromPromOption(po PromOption) (api.Client, error) { + return api.NewClient(api.Config{ + Address: po.WriteAddr, + RoundTripper: &http.Transport{ + // TLSClientConfig: tlsConfig, + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: time.Duration(po.DialTimeout) * time.Millisecond, + }).DialContext, + ResponseHeaderTimeout: time.Duration(po.Timeout) * time.Millisecond, + MaxIdleConnsPerHost: po.MaxIdleConnsPerHost, + }, + }) +} + +func (pc *PromClientMap) setClientFromPromOption(datasourceId int64, po PromOption) error { + if datasourceId < 0 { + return fmt.Errorf("argument clusterName is blank") + } + + if po.Url == "" { + return fmt.Errorf("prometheus url is blank") + } + + readerCli, err := pc.newReaderClientFromPromOption(po) + if err != nil { + return fmt.Errorf("failed to newClientFromPromOption: %v", err) + } + + reader := prom.NewAPI(readerCli, prom.ClientOptions{ + BasicAuthUser: po.BasicAuthUser, + BasicAuthPass: po.BasicAuthPass, + Headers: po.Headers, + }) + + writerCli, err := pc.newWriterClientFromPromOption(po) + if err != nil { + return fmt.Errorf("failed to newClientFromPromOption: %v", err) + } + + w := prom.NewWriter(writerCli, prom.ClientOptions{ + Url: po.WriteAddr, + BasicAuthUser: po.BasicAuthUser, + BasicAuthPass: po.BasicAuthPass, + Headers: po.Headers, + }) + + logger.Debugf("setClientFromPromOption: %d, %+v", datasourceId, po) + pc.Set(datasourceId, reader, w) + + return nil +} diff --git a/pushgw/idents/idents.go b/pushgw/idents/idents.go new file mode 100644 index 0000000000000000000000000000000000000000..0c744c2cceb957f72c180fad6252e206659451b1 --- /dev/null +++ b/pushgw/idents/idents.go @@ -0,0 +1,178 @@ +package idents + +import ( + "math" + "sync" + "time" + + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/slice" + "gorm.io/gorm" +) + +type Set struct { + sync.Mutex + items map[string]int64 + db *gorm.DB + datasourceId int64 + maxOffset int64 +} + +func New(db *gorm.DB, dsId, maxOffset int64) *Set { + if maxOffset <= 0 { + maxOffset = 500 + } + set := &Set{ + items: make(map[string]int64), + db: db, + datasourceId: dsId, + maxOffset: maxOffset, + } + + set.Init() + return set +} + +func (s *Set) Init() { + go s.LoopPersist() +} + +func (s *Set) MSet(items map[string]int64) { + s.Lock() + defer s.Unlock() + for ident, ts := range items { + s.items[ident] = ts + } +} + +func (s *Set) LoopPersist() { + for { + time.Sleep(time.Second) + s.persist() + } +} + +func (s *Set) persist() { + var items map[string]int64 + + s.Lock() + if len(s.items) == 0 { + s.Unlock() + return + } + + items = s.items + s.items = make(map[string]int64) + s.Unlock() + + s.updateTimestamp(items) +} + +func (s *Set) updateTimestamp(items map[string]int64) { + lst := make([]string, 0, 100) + offsetLst := make(map[string]int64) + now := time.Now().Unix() + num := 0 + + largeOffsetTargets, _ := s.GetLargeOffsetTargets() + + for ident, ts := range items { + // 和当前时间相差 maxOffset 毫秒以上的,更新偏移的时间 + // compare with current time, if offset is larger than maxOffset, update offset + offset := int64(math.Abs(float64(ts - time.Now().UnixMilli()))) + if offset >= s.maxOffset { + offsetLst[ident] = offset + } + + // 如果是大偏移的,也更新时间 + // if offset is large, update timestamp + if _, ok := largeOffsetTargets[ident]; ok { + offsetLst[ident] = offset + } + + lst = append(lst, ident) + num++ + if num == 100 { + if err := s.updateTargets(lst, now); err != nil { + logger.Errorf("failed to update targets: %v", err) + } + lst = lst[:0] + num = 0 + } + } + + if err := s.updateTargets(lst, now); err != nil { + logger.Errorf("failed to update targets: %v", err) + } + + for ident, offset := range offsetLst { + if err := s.updateTargetsAndOffset(ident, offset, now); err != nil { + logger.Errorf("failed to update offset: %v", err) + } + } +} + +func (s *Set) updateTargets(lst []string, now int64) error { + count := int64(len(lst)) + if count == 0 { + return nil + } + + ret := s.db.Table("target").Where("ident in ?", lst).Update("update_at", now) + if ret.Error != nil { + return ret.Error + } + + if ret.RowsAffected == count { + return nil + } + + // there are some idents not found in db, so insert them + var exists []string + err := s.db.Table("target").Where("ident in ?", lst).Pluck("ident", &exists).Error + if err != nil { + return err + } + + news := slice.SubString(lst, exists) + for i := 0; i < len(news); i++ { + err = s.db.Exec("INSERT INTO target(ident, update_at, datasource_id) VALUES(?, ?, ?)", news[i], now, s.datasourceId).Error + if err != nil { + logger.Error("failed to insert target:", news[i], "error:", err) + } + } + + return nil +} + +func (s *Set) updateTargetsAndOffset(ident string, offset, now int64) error { + ret := s.db.Table("target").Where("ident = ?", ident).Updates(map[string]interface{}{"update_at": now, "offset": offset}) + if ret.Error != nil { + return ret.Error + } + if ret.RowsAffected == 1 { + return nil + } + + // there are some idents not found in db, so insert them + err := s.db.Exec("INSERT INTO target(ident, offset, update_at, datasource_id) VALUES(?, ?, ?, ?)", ident, offset, now, s.datasourceId).Error + if err != nil { + logger.Error("failed to insert target:", ident, "error:", err) + } + + return nil +} + +func (s *Set) GetLargeOffsetTargets() (map[string]struct{}, error) { + var targets []string + err := s.db.Table("target").Where("offset > ?", s.maxOffset).Pluck("ident", &targets).Error + if err != nil { + return nil, err + } + + var m = make(map[string]struct{}, len(targets)) + for i := 0; i < len(targets); i++ { + m[targets[i]] = struct{}{} + } + return m, nil +} diff --git a/pushgw/pconf/conf.go b/pushgw/pconf/conf.go new file mode 100644 index 0000000000000000000000000000000000000000..7028c1006eafc6541f7ea150c519cad516515d23 --- /dev/null +++ b/pushgw/pconf/conf.go @@ -0,0 +1,111 @@ +package pconf + +import ( + "log" + "regexp" + + "github.com/ccfos/nightingale/v6/pkg/tlsx" + + "github.com/prometheus/common/model" +) + +type Pushgw struct { + DatasourceId int64 + BusiGroupLabelKey string + MaxOffset int64 + LabelRewrite bool + ForceUseServerTS bool + DebugSample map[string]string + WriterOpt WriterGlobalOpt + Writers []WriterOptions +} + +type WriterGlobalOpt struct { + QueueCount int + QueueMaxSize int + QueuePopSize int + ShardingKey string +} + +type WriterOptions struct { + Url string + BasicAuthUser string + BasicAuthPass string + + Timeout int64 + DialTimeout int64 + TLSHandshakeTimeout int64 + ExpectContinueTimeout int64 + IdleConnTimeout int64 + KeepAlive int64 + + MaxConnsPerHost int + MaxIdleConns int + MaxIdleConnsPerHost int + + Headers []string + + WriteRelabels []*RelabelConfig + + tlsx.ClientConfig +} + +type RelabelConfig struct { + SourceLabels model.LabelNames + Separator string + Regex string + RegexCompiled *regexp.Regexp + Modulus uint64 + TargetLabel string + Replacement string + Action string +} + +func (p *Pushgw) PreCheck() { + if p.BusiGroupLabelKey == "" { + p.BusiGroupLabelKey = "busigroup" + } + + if p.WriterOpt.QueueMaxSize <= 0 { + p.WriterOpt.QueueMaxSize = 10000000 + } + + if p.WriterOpt.QueuePopSize <= 0 { + p.WriterOpt.QueuePopSize = 1000 + } + + if p.WriterOpt.QueueCount <= 0 { + p.WriterOpt.QueueCount = 1000 + } + + if p.WriterOpt.ShardingKey == "" { + p.WriterOpt.ShardingKey = "ident" + } + + for _, writer := range p.Writers { + for _, relabel := range writer.WriteRelabels { + if relabel.Regex == "" { + relabel.Regex = "(.*)" + } + + regex, err := regexp.Compile("^(?:" + relabel.Regex + ")$") + if err != nil { + log.Fatalln("failed to compile regexp:", relabel.Regex, "error:", err) + } + + relabel.RegexCompiled = regex + + if relabel.Separator == "" { + relabel.Separator = ";" + } + + if relabel.Action == "" { + relabel.Action = "replace" + } + + if relabel.Replacement == "" { + relabel.Replacement = "$1" + } + } + } +} diff --git a/pushgw/pushgw.go b/pushgw/pushgw.go new file mode 100644 index 0000000000000000000000000000000000000000..0d1b9c0c20ae4a192e5f2911eb85e3ba3ff96e29 --- /dev/null +++ b/pushgw/pushgw.go @@ -0,0 +1,58 @@ +package pushgw + +import ( + "context" + "fmt" + + "github.com/ccfos/nightingale/v6/conf" + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "github.com/ccfos/nightingale/v6/pkg/httpx" + "github.com/ccfos/nightingale/v6/pkg/logx" + "github.com/ccfos/nightingale/v6/pushgw/idents" + "github.com/ccfos/nightingale/v6/pushgw/router" + "github.com/ccfos/nightingale/v6/pushgw/writer" + "github.com/ccfos/nightingale/v6/storage" +) + +type PushgwProvider struct { + Ident *idents.Set + Router *router.Router +} + +func Initialize(configDir string, cryptoKey string) (func(), error) { + config, err := conf.InitConfig(configDir, cryptoKey) + if err != nil { + return nil, fmt.Errorf("failed to init config: %v", err) + } + + logxClean, err := logx.Init(config.Log) + if err != nil { + return nil, err + } + + db, err := storage.New(config.DB) + if err != nil { + return nil, err + } + ctx := ctx.NewContext(context.Background(), db) + + idents := idents.New(db, config.Pushgw.DatasourceId, config.Pushgw.MaxOffset) + stats := memsto.NewSyncStats() + + busiGroupCache := memsto.NewBusiGroupCache(ctx, stats) + targetCache := memsto.NewTargetCache(ctx, stats) + + writers := writer.NewWriters(config.Pushgw) + + r := httpx.GinEngine(config.Global.RunMode, config.HTTP) + rt := router.New(config.HTTP, config.Pushgw, targetCache, busiGroupCache, idents, writers, ctx) + rt.Config(r) + + httpClean := httpx.Init(config.HTTP, r) + + return func() { + logxClean() + httpClean() + }, nil +} diff --git a/pushgw/router/fns.go b/pushgw/router/fns.go new file mode 100644 index 0000000000000000000000000000000000000000..fff637f719c7b26cadbaab89d98067e82e518adf --- /dev/null +++ b/pushgw/router/fns.go @@ -0,0 +1,98 @@ +package router + +import ( + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/models" + + "github.com/prometheus/prometheus/prompb" + "github.com/toolkits/pkg/logger" +) + +func (rt *Router) AppendLabels(pt *prompb.TimeSeries, target *models.Target, bgCache *memsto.BusiGroupCacheType) { + if target == nil { + return + } + + labelKeys := make(map[string]int) + for j := 0; j < len(pt.Labels); j++ { + labelKeys[pt.Labels[j].Name] = j + } + + for key, value := range target.TagsMap { + if index, has := labelKeys[key]; has { + // overwrite labels + if rt.Pushgw.LabelRewrite { + pt.Labels[index].Value = value + } + continue + } + + pt.Labels = append(pt.Labels, &prompb.Label{ + Name: key, + Value: value, + }) + } + + // e.g. busigroup=cloud + if _, has := labelKeys[rt.Pushgw.BusiGroupLabelKey]; has { + return + } + + // append busigroup tags + if target.GroupId > 0 && len(rt.Pushgw.BusiGroupLabelKey) > 0 { + bg := bgCache.GetByBusiGroupId(target.GroupId) + if bg == nil { + return + } + + if bg.LabelEnable == 0 { + return + } + + if index, has := labelKeys[rt.Pushgw.BusiGroupLabelKey]; has { + // overwrite labels + if rt.Pushgw.LabelRewrite { + pt.Labels[index].Value = bg.LabelValue + } + return + } + + pt.Labels = append(pt.Labels, &prompb.Label{ + Name: rt.Pushgw.BusiGroupLabelKey, + Value: bg.LabelValue, + }) + } +} + +func getTs(pt *prompb.TimeSeries) int64 { + if len(pt.Samples) == 0 { + return 0 + } + + return pt.Samples[0].Timestamp +} + +func (rt *Router) debugSample(remoteAddr string, v *prompb.TimeSeries) { + filter := rt.Pushgw.DebugSample + if len(filter) == 0 { + return + } + + labelMap := make(map[string]string) + for i := 0; i < len(v.Labels); i++ { + labelMap[v.Labels[i].Name] = v.Labels[i].Value + } + + for k, v := range filter { + labelValue, exists := labelMap[k] + if !exists { + return + } + + if labelValue != v { + return + } + } + + logger.Debugf("--> debug sample from: %s, sample: %s", remoteAddr, v.String()) +} diff --git a/pushgw/router/router.go b/pushgw/router/router.go new file mode 100644 index 0000000000000000000000000000000000000000..eafa712817136da094d234c473b42d2e885781b2 --- /dev/null +++ b/pushgw/router/router.go @@ -0,0 +1,58 @@ +package router + +import ( + "github.com/gin-gonic/gin" + + "github.com/ccfos/nightingale/v6/memsto" + "github.com/ccfos/nightingale/v6/pkg/ctx" + "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/writer" +) + +type Router struct { + HTTP httpx.Config + Pushgw pconf.Pushgw + TargetCache *memsto.TargetCacheType + BusiGroupCache *memsto.BusiGroupCacheType + IdentSet *idents.Set + Writers *writer.WritersType + Ctx *ctx.Context +} + +func New(httpConfig httpx.Config, pushgw pconf.Pushgw, tc *memsto.TargetCacheType, bg *memsto.BusiGroupCacheType, set *idents.Set, writers *writer.WritersType, ctx *ctx.Context) *Router { + return &Router{ + IdentSet: set, + HTTP: httpConfig, + Writers: writers, + Ctx: ctx, + TargetCache: tc, + BusiGroupCache: bg, + } +} + +func (rt *Router) Config(r *gin.Engine) { + registerMetrics() + + // datadog url: http://n9e-pushgw.foo.com/datadog + // use apiKey not basic auth + r.POST("/datadog/api/v1/series", rt.datadogSeries) + r.POST("/datadog/api/v1/check_run", datadogCheckRun) + r.GET("/datadog/api/v1/validate", datadogValidate) + r.POST("/datadog/api/v1/metadata", datadogMetadata) + r.POST("/datadog/intake/", datadogIntake) + + if len(rt.HTTP.BasicAuth) > 0 { + // enable basic auth + auth := gin.BasicAuth(rt.HTTP.BasicAuth) + r.POST("/opentsdb/put", auth, rt.openTSDBPut) + r.POST("/openfalcon/push", auth, rt.falconPush) + r.POST("/prometheus/v1/write", auth, rt.remoteWrite) + } else { + // no need basic auth + r.POST("/opentsdb/put", rt.openTSDBPut) + r.POST("/openfalcon/push", rt.falconPush) + r.POST("/prometheus/v1/write", rt.remoteWrite) + } +} diff --git a/src/server/router/router_datadog.go b/pushgw/router/router_datadog.go similarity index 79% rename from src/server/router/router_datadog.go rename to pushgw/router/router_datadog.go index c040ba537f441d0e389cda3c0a3fb9f15511e136..74262eb35366aab59b074c619c9730922adb3707 100644 --- a/src/server/router/router_datadog.go +++ b/pushgw/router/router_datadog.go @@ -7,16 +7,9 @@ import ( "io/ioutil" "net/http" "strings" - "time" - - "github.com/didi/nightingale/v5/src/server/common" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/idents" - "github.com/didi/nightingale/v5/src/server/memsto" - promstat "github.com/didi/nightingale/v5/src/server/stat" - "github.com/didi/nightingale/v5/src/server/writer" + "github.com/gin-gonic/gin" - "github.com/mailru/easyjson" + easyjson "github.com/mailru/easyjson" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/prompb" ) @@ -83,7 +76,6 @@ func (m *DatadogMetric) ToProm() (*prompb.TimeSeries, string, error) { key := arr[0] if key == "ident" { - // 如果tags中有ident,那就用 identInTag = arr[1] pt.Labels = append(pt.Labels, &prompb.Label{ Name: key, @@ -116,7 +108,7 @@ func (m *DatadogMetric) ToProm() (*prompb.TimeSeries, string, error) { } if m.Host != "" { - // 以外层为准,外层host字段覆盖标签中的host + // m.Host has high priority hostInTag = m.Host } @@ -188,16 +180,15 @@ func readDatadogBody(c *gin.Context) ([]byte, error) { return bs, err } -func datadogSeries(c *gin.Context) { +func (r *Router) datadogSeries(c *gin.Context) { apiKey, has := c.GetQuery("api_key") if !has { apiKey = "" } - if len(config.C.BasicAuth) > 0 { - // n9e-server need basic auth + if len(r.HTTP.BasicAuth) > 0 { ok := false - for _, v := range config.C.BasicAuth { + for _, v := range r.HTTP.BasicAuth { if apiKey == v { ok = true break @@ -232,9 +223,8 @@ func datadogSeries(c *gin.Context) { var ( succ int fail int - msg = "data pushed to queue" - ts = time.Now().Unix() - ids = make(map[string]interface{}) + msg = "received" + ids = make(map[string]int64) ) for i := 0; i < cnt; i++ { @@ -257,35 +247,33 @@ func datadogSeries(c *gin.Context) { if ident != "" { // register host - ids[ident] = ts + ids[ident] = getTs(pt) // fill tags - target, has := memsto.TargetCache.Get(ident) + target, has := r.TargetCache.Get(ident) if has { - common.AppendLabels(pt, target) + r.AppendLabels(pt, target, r.BusiGroupCache) } } - LogSample(c.Request.RemoteAddr, pt) - if config.C.WriterOpt.ShardingKey == "ident" { + r.debugSample(c.Request.RemoteAddr, pt) + + if r.Pushgw.WriterOpt.ShardingKey == "ident" { if ident == "" { - writer.Writers.PushSample("-", pt) + r.Writers.PushSample("-", pt) } else { - writer.Writers.PushSample(ident, pt) + r.Writers.PushSample(ident, pt) } } else { - writer.Writers.PushSample(item.Metric, pt) + r.Writers.PushSample(item.Metric, pt) } succ++ } if succ > 0 { - cn := config.C.ClusterName - if cn != "" { - promstat.CounterSampleTotal.WithLabelValues(cn, "datadog").Add(float64(succ)) - } - idents.Idents.MSet(ids) + CounterSampleTotal.WithLabelValues("datadog").Add(float64(succ)) + r.IdentSet.MSet(ids) } c.JSON(200, gin.H{ diff --git a/src/server/router/router_datadog_easyjson.go b/pushgw/router/router_datadog_easyjson.go similarity index 100% rename from src/server/router/router_datadog_easyjson.go rename to pushgw/router/router_datadog_easyjson.go diff --git a/src/server/router/router_openfalcon.go b/pushgw/router/router_openfalcon.go similarity index 80% rename from src/server/router/router_openfalcon.go rename to pushgw/router/router_openfalcon.go index 4a5d572614c353560ca88e7c943283092e089a9b..38db8cb04e39119dd4dd79209854c868caa1fe53 100644 --- a/src/server/router/router_openfalcon.go +++ b/pushgw/router/router_openfalcon.go @@ -8,12 +8,6 @@ import ( "strings" "time" - "github.com/didi/nightingale/v5/src/server/common" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/idents" - "github.com/didi/nightingale/v5/src/server/memsto" - promstat "github.com/didi/nightingale/v5/src/server/stat" - "github.com/didi/nightingale/v5/src/server/writer" "github.com/gin-gonic/gin" "github.com/mailru/easyjson" "github.com/prometheus/common/model" @@ -113,10 +107,10 @@ func (m *FalconMetric) ToProm() (*prompb.TimeSeries, string, error) { ident = m.Endpoint if id, exists := tagmap["ident"]; exists { ident = id - // 以tags中的ident作为唯一标识 + // use ident in tags tagmap["endpoint"] = m.Endpoint } else { - // 把endpoint塞到tags中,改key为ident + // use endpoint as ident tagmap["ident"] = m.Endpoint } } @@ -143,8 +137,7 @@ func (m *FalconMetric) ToProm() (*prompb.TimeSeries, string, error) { return pt, ident, nil } -func falconPush(c *gin.Context) { - +func (rt *Router) falconPush(c *gin.Context) { var bs []byte var err error var r *gzip.Reader @@ -185,9 +178,9 @@ func falconPush(c *gin.Context) { var ( succ int fail int - msg = "data pushed to queue" + msg = "received" ts = time.Now().Unix() - ids = make(map[string]interface{}) + ids = make(map[string]int64) ) for i := 0; i < len(arr); i++ { @@ -204,36 +197,32 @@ func falconPush(c *gin.Context) { if ident != "" { // register host - ids[ident] = ts - + ids[ident] = getTs(pt) // fill tags - target, has := memsto.TargetCache.Get(ident) + target, has := rt.TargetCache.Get(ident) if has { - common.AppendLabels(pt, target) + rt.AppendLabels(pt, target, rt.BusiGroupCache) } } - LogSample(c.Request.RemoteAddr, pt) - if config.C.WriterOpt.ShardingKey == "ident" { + rt.debugSample(c.Request.RemoteAddr, pt) + + if rt.Pushgw.WriterOpt.ShardingKey == "ident" { if ident == "" { - writer.Writers.PushSample("-", pt) + rt.Writers.PushSample("-", pt) } else { - writer.Writers.PushSample(ident, pt) + rt.Writers.PushSample(ident, pt) } } else { - writer.Writers.PushSample(arr[i].Metric, pt) + rt.Writers.PushSample(arr[i].Metric, pt) } succ++ } if succ > 0 { - cn := config.C.ClusterName - if cn != "" { - promstat.CounterSampleTotal.WithLabelValues(cn, "openfalcon").Add(float64(succ)) - } - - idents.Idents.MSet(ids) + CounterSampleTotal.WithLabelValues("openfalcon").Add(float64(succ)) + rt.IdentSet.MSet(ids) } c.JSON(200, gin.H{ diff --git a/src/server/router/router_openfalcon_easyjson.go b/pushgw/router/router_openfalcon_easyjson.go similarity index 100% rename from src/server/router/router_openfalcon_easyjson.go rename to pushgw/router/router_openfalcon_easyjson.go diff --git a/src/server/router/router_opentsdb.go b/pushgw/router/router_opentsdb.go similarity index 80% rename from src/server/router/router_opentsdb.go rename to pushgw/router/router_opentsdb.go index ecae52866bd8f6f7028b3f75a91a5e669c1f8dca..0411394769028fe68af1580f38ffec4f79b2b0ae 100644 --- a/src/server/router/router_opentsdb.go +++ b/pushgw/router/router_opentsdb.go @@ -13,12 +13,6 @@ import ( "github.com/prometheus/prometheus/prompb" "github.com/toolkits/pkg/logger" - "github.com/didi/nightingale/v5/src/server/common" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/idents" - "github.com/didi/nightingale/v5/src/server/memsto" - promstat "github.com/didi/nightingale/v5/src/server/stat" - "github.com/didi/nightingale/v5/src/server/writer" "github.com/mailru/easyjson" _ "github.com/mailru/easyjson/gen" ) @@ -128,7 +122,7 @@ func (m *HTTPMetric) ToProm() (*prompb.TimeSeries, error) { return pt, nil } -func handleOpenTSDB(c *gin.Context) { +func (rt *Router) openTSDBPut(c *gin.Context) { var bs []byte var err error var r *gzip.Reader @@ -170,9 +164,9 @@ func handleOpenTSDB(c *gin.Context) { var ( succ int fail int - msg = "data pushed to queue" + msg = "received" ts = time.Now().Unix() - ids = make(map[string]interface{}) + ids = make(map[string]int64) ) for i := 0; i < len(arr); i++ { @@ -198,39 +192,33 @@ func handleOpenTSDB(c *gin.Context) { host, has := arr[i].Tags["ident"] if has { // register host - ids[host] = ts + ids[host] = getTs(pt) // fill tags - target, has := memsto.TargetCache.Get(host) + target, has := rt.TargetCache.Get(host) if has { - common.AppendLabels(pt, target) + rt.AppendLabels(pt, target, rt.BusiGroupCache) } } - LogSample(c.Request.RemoteAddr, pt) - if config.C.WriterOpt.ShardingKey == "ident" { + rt.debugSample(c.Request.RemoteAddr, pt) + + if rt.Pushgw.WriterOpt.ShardingKey == "ident" { if host == "" { - writer.Writers.PushSample("-", pt) + rt.Writers.PushSample("-", pt) } else { - writer.Writers.PushSample(host, pt) + rt.Writers.PushSample(host, pt) } } else { - writer.Writers.PushSample(arr[i].Metric, pt) + rt.Writers.PushSample(arr[i].Metric, pt) } succ++ } if succ > 0 { - cn := config.C.ClusterName - if cn != "" { - promstat.CounterSampleTotal.WithLabelValues(cn, "opentsdb").Add(float64(succ)) - } - idents.Idents.MSet(ids) - } - - if fail > 0 { - logger.Debugf("opentsdb msg process error , msg is : %s", string(bs)) + CounterSampleTotal.WithLabelValues("opentsdb").Add(float64(succ)) + rt.IdentSet.MSet(ids) } c.JSON(200, gin.H{ diff --git a/src/server/router/router_opentsdb_easyjson.go b/pushgw/router/router_opentsdb_easyjson.go similarity index 100% rename from src/server/router/router_opentsdb_easyjson.go rename to pushgw/router/router_opentsdb_easyjson.go diff --git a/src/server/router/router_prom.go b/pushgw/router/router_remotewrite.go similarity index 50% rename from src/server/router/router_prom.go rename to pushgw/router/router_remotewrite.go index bef6e85ee91a933c368708b0b0d348d4e8dd22cc..e135dbdce59c37a3e9a77754858ef038db8885f0 100644 --- a/src/server/router/router_prom.go +++ b/pushgw/router/router_remotewrite.go @@ -4,21 +4,11 @@ import ( "io" "io/ioutil" "net/http" - "time" "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/didi/nightingale/v5/src/server/common" - "github.com/didi/nightingale/v5/src/server/common/conv" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/idents" - "github.com/didi/nightingale/v5/src/server/memsto" - promstat "github.com/didi/nightingale/v5/src/server/stat" - "github.com/didi/nightingale/v5/src/server/writer" ) var promMetricFilter map[string]bool = map[string]bool{ @@ -29,33 +19,6 @@ var promMetricFilter map[string]bool = map[string]bool{ "scrape_duration_seconds": true, } -type promqlForm struct { - PromQL string `json:"promql"` -} - -func queryPromql(c *gin.Context) { - var f promqlForm - ginx.BindJSON(c, &f) - - if config.ReaderClients.IsNil(config.C.ClusterName) { - c.String(500, "reader client is nil") - return - } - - value, warnings, err := config.ReaderClients.GetCli(config.C.ClusterName).Query(c.Request.Context(), f.PromQL, time.Now()) - if err != nil { - c.String(500, "promql:%s error:%v", f.PromQL, err) - return - } - - if len(warnings) > 0 { - c.String(500, "promql:%s warnings:%v", f.PromQL, warnings) - return - } - - c.JSON(200, conv.ConvertVectors(value)) -} - func duplicateLabelKey(series *prompb.TimeSeries) bool { if series == nil { return false @@ -74,7 +37,7 @@ func duplicateLabelKey(series *prompb.TimeSeries) bool { return false } -func remoteWrite(c *gin.Context) { +func (rt *Router) remoteWrite(c *gin.Context) { req, err := DecodeWriteRequest(c.Request.Body) if err != nil { c.String(http.StatusBadRequest, err.Error()) @@ -89,8 +52,7 @@ func remoteWrite(c *gin.Context) { } var ( - now = time.Now().Unix() - ids = make(map[string]interface{}) + ids = make(map[string]int64) ident string metric string ) @@ -104,10 +66,12 @@ func remoteWrite(c *gin.Context) { // find ident label for j := 0; j < len(req.Timeseries[i].Labels); j++ { + if req.Timeseries[i].Labels[j].Name == "host" { + req.Timeseries[i].Labels[j].Name = "ident" + } + if req.Timeseries[i].Labels[j].Name == "ident" { ident = req.Timeseries[i].Labels[j].Value - } else if req.Timeseries[i].Labels[j].Name == "host" { - ident = req.Timeseries[i].Labels[j].Value } if req.Timeseries[i].Labels[j].Name == "__name__" { @@ -126,44 +90,35 @@ func remoteWrite(c *gin.Context) { } } - // 当数据是通过prometheus抓取(也许直接remote write到夜莺)的时候,prometheus会自动产生部分系统指标 - // 例如最典型的有up指标,是prometheus为exporter生成的指标,即使exporter挂掉的时候也会送up=0的指标 - // 此类指标当剔除,否则会导致redis数据中时间戳被意外更新,导致由此类指标中携带的ident的相关target_up指标无法变为实际的0值 - // 更多详细信息:https://prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series - if _, has := promMetricFilter[metric]; has { - ident = "" - } - if len(ident) > 0 { // register host - ids[ident] = now + // https://prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series + if _, has := promMetricFilter[metric]; !has { + ids[ident] = getTs(req.Timeseries[i]) + } // fill tags - target, has := memsto.TargetCache.Get(ident) + target, has := rt.TargetCache.Get(ident) if has { - common.AppendLabels(req.Timeseries[i], target) + rt.AppendLabels(req.Timeseries[i], target, rt.BusiGroupCache) } } - LogSample(c.Request.RemoteAddr, req.Timeseries[i]) + rt.debugSample(c.Request.RemoteAddr, req.Timeseries[i]) - if config.C.WriterOpt.ShardingKey == "ident" { + if rt.Pushgw.WriterOpt.ShardingKey == "ident" { if ident == "" { - writer.Writers.PushSample("-", req.Timeseries[i]) + rt.Writers.PushSample("-", req.Timeseries[i]) } else { - writer.Writers.PushSample(ident, req.Timeseries[i]) + rt.Writers.PushSample(ident, req.Timeseries[i]) } } else { - writer.Writers.PushSample(metric, req.Timeseries[i]) + rt.Writers.PushSample(metric, req.Timeseries[i]) } } - cn := config.C.ClusterName - if cn != "" { - promstat.CounterSampleTotal.WithLabelValues(cn, "prometheus").Add(float64(count)) - } - - idents.Idents.MSet(ids) + CounterSampleTotal.WithLabelValues("prometheus").Add(float64(count)) + rt.IdentSet.MSet(ids) } // DecodeWriteRequest from an io.Reader into a prompb.WriteRequest, handling diff --git a/pushgw/router/stat.go b/pushgw/router/stat.go new file mode 100644 index 0000000000000000000000000000000000000000..e995aee6bf6118429ffb72b51e9249f42fdf6755 --- /dev/null +++ b/pushgw/router/stat.go @@ -0,0 +1,18 @@ +package router + +import "github.com/prometheus/client_golang/prometheus" + +var ( + CounterSampleTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: "n9e", + Subsystem: "pushgw", + Name: "samples_received_total", + Help: "Total number samples received.", + }, []string{"channel"}) +) + +func registerMetrics() { + prometheus.MustRegister( + CounterSampleTotal, + ) +} diff --git a/src/server/writer/queue.go b/pushgw/writer/queue.go similarity index 100% rename from src/server/writer/queue.go rename to pushgw/writer/queue.go diff --git a/src/models/relabel.go b/pushgw/writer/relabel.go similarity index 75% rename from src/models/relabel.go rename to pushgw/writer/relabel.go index 59e71675e270794100fe79e182cbfaf188343ee6..c574f13ac083b20bd3c11d4774767f008bd51803 100644 --- a/src/models/relabel.go +++ b/pushgw/writer/relabel.go @@ -1,45 +1,29 @@ -package models +package writer import ( "crypto/md5" "fmt" - "regexp" "sort" "strings" + "github.com/ccfos/nightingale/v6/pushgw/pconf" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/prompb" ) const ( - Replace Action = "replace" - Keep Action = "keep" - Drop Action = "drop" - HashMod Action = "hashmod" - LabelMap Action = "labelmap" - LabelDrop Action = "labeldrop" - LabelKeep Action = "labelkeep" - Lowercase Action = "lowercase" - Uppercase Action = "uppercase" + Replace string = "replace" + Keep string = "keep" + Drop string = "drop" + HashMod string = "hashmod" + LabelMap string = "labelmap" + LabelDrop string = "labeldrop" + LabelKeep string = "labelkeep" + Lowercase string = "lowercase" + Uppercase string = "uppercase" ) -type Action string - -type Regexp struct { - *regexp.Regexp -} - -type RelabelConfig struct { - SourceLabels model.LabelNames - Separator string - Regex interface{} - Modulus uint64 - TargetLabel string - Replacement string - Action Action -} - -func Process(labels []*prompb.Label, cfgs ...*RelabelConfig) []*prompb.Label { +func Process(labels []*prompb.Label, cfgs ...*pconf.RelabelConfig) []*prompb.Label { for _, cfg := range cfgs { labels = relabel(labels, cfg) if labels == nil { @@ -105,13 +89,13 @@ func (l *LabelBuilder) labels() []*prompb.Label { return ls } -func relabel(lset []*prompb.Label, cfg *RelabelConfig) []*prompb.Label { +func relabel(lset []*prompb.Label, cfg *pconf.RelabelConfig) []*prompb.Label { values := make([]string, 0, len(cfg.SourceLabels)) for _, ln := range cfg.SourceLabels { values = append(values, getValue(lset, ln)) } - regx := cfg.Regex.(Regexp) + regx := cfg.RegexCompiled val := strings.Join(values, cfg.Separator) lb := newBuilder(lset) @@ -183,16 +167,3 @@ func sum64(hash [md5.Size]byte) uint64 { } return s } - -func NewRegexp(s string) (Regexp, error) { - regex, err := regexp.Compile("^(?:" + s + ")$") - return Regexp{Regexp: regex}, err -} - -func MustNewRegexp(s string) Regexp { - re, err := NewRegexp(s) - if err != nil { - panic(err) - } - return re -} diff --git a/pushgw/writer/writer.go b/pushgw/writer/writer.go new file mode 100644 index 0000000000000000000000000000000000000000..a4d94bdb77b9b892db90a2d1b902d539179e5960 --- /dev/null +++ b/pushgw/writer/writer.go @@ -0,0 +1,220 @@ +package writer + +import ( + "bytes" + "context" + "fmt" + "hash/crc32" + "net" + "net/http" + "time" + + "github.com/ccfos/nightingale/v6/pushgw/pconf" + + "github.com/golang/protobuf/proto" + "github.com/golang/snappy" + "github.com/prometheus/client_golang/api" + "github.com/prometheus/prometheus/prompb" + "github.com/toolkits/pkg/logger" +) + +type WriterType struct { + Opts pconf.WriterOptions + ForceUseServerTS bool + Client api.Client +} + +func (w WriterType) writeRelabel(items []*prompb.TimeSeries) []*prompb.TimeSeries { + ritems := make([]*prompb.TimeSeries, 0, len(items)) + for _, item := range items { + lbls := Process(item.Labels, w.Opts.WriteRelabels...) + if len(lbls) == 0 { + continue + } + ritems = append(ritems, item) + } + return ritems +} + +func (w WriterType) Write(index int, items []*prompb.TimeSeries, headers ...map[string]string) { + if len(items) == 0 { + return + } + + items = w.writeRelabel(items) + if len(items) == 0 { + return + } + + if w.ForceUseServerTS { + ts := time.Now().UnixMilli() + for i := 0; i < len(items); i++ { + if len(items[i].Samples) == 0 { + continue + } + items[i].Samples[0].Timestamp = ts + } + } + + req := &prompb.WriteRequest{ + Timeseries: items, + } + + data, err := proto.Marshal(req) + if err != nil { + logger.Warningf("marshal prom data to proto got error: %v, data: %+v", err, items) + return + } + + if err := w.Post(snappy.Encode(nil, data), headers...); err != nil { + logger.Warningf("post to %s got error: %v", w.Opts.Url, err) + logger.Warning("example timeseries:", items[0].String()) + } +} + +func (w WriterType) Post(req []byte, headers ...map[string]string) error { + httpReq, err := http.NewRequest("POST", w.Opts.Url, bytes.NewReader(req)) + if err != nil { + logger.Warningf("create remote write request got error: %s", err.Error()) + return err + } + + httpReq.Header.Add("Content-Encoding", "snappy") + httpReq.Header.Set("Content-Type", "application/x-protobuf") + httpReq.Header.Set("User-Agent", "n9e") + httpReq.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0") + + if len(headers) > 0 { + for k, v := range headers[0] { + httpReq.Header.Set(k, v) + } + } + + if w.Opts.BasicAuthUser != "" { + httpReq.SetBasicAuth(w.Opts.BasicAuthUser, w.Opts.BasicAuthPass) + } + + headerCount := len(w.Opts.Headers) + if headerCount > 0 && headerCount%2 == 0 { + for i := 0; i < len(w.Opts.Headers); i += 2 { + httpReq.Header.Add(w.Opts.Headers[i], w.Opts.Headers[i+1]) + if w.Opts.Headers[i] == "Host" { + httpReq.Host = w.Opts.Headers[i+1] + } + } + } + + resp, body, err := w.Client.Do(context.Background(), httpReq) + if err != nil { + logger.Warningf("push data with remote write request got error: %v, response body: %s", err, string(body)) + return err + } + + if resp.StatusCode >= 400 { + err = fmt.Errorf("push data with remote write request got status code: %v, response body: %s", resp.StatusCode, string(body)) + return err + } + + return nil +} + +type WritersType struct { + pushgw pconf.Pushgw + backends map[string]WriterType + queues map[int]*SafeListLimited +} + +func NewWriters(pushgwConfig pconf.Pushgw) *WritersType { + writers := &WritersType{ + backends: make(map[string]WriterType), + pushgw: pushgwConfig, + } + + writers.Init() + return writers +} + +func (ws *WritersType) Put(name string, writer WriterType) { + ws.backends[name] = writer +} + +func (ws *WritersType) PushSample(ident string, v interface{}) { + hashkey := crc32.ChecksumIEEE([]byte(ident)) % uint32(ws.pushgw.WriterOpt.QueueCount) + + c, ok := ws.queues[int(hashkey)] + if ok { + succ := c.PushFront(v) + if !succ { + logger.Warningf("Write channel(%s) full, current channel size: %d", ident, c.Len()) + } + } +} + +func (ws *WritersType) StartConsumer(index int, ch *SafeListLimited) { + for { + series := ch.PopBack(ws.pushgw.WriterOpt.QueuePopSize) + if len(series) == 0 { + time.Sleep(time.Millisecond * 400) + continue + } + + for key := range ws.backends { + go ws.backends[key].Write(index, series) + } + } +} + +func (ws *WritersType) Init() error { + opts := ws.pushgw.Writers + ws.queues = make(map[int]*SafeListLimited) + + for i := 0; i < ws.pushgw.WriterOpt.QueueCount; i++ { + ws.queues[i] = NewSafeListLimited(ws.pushgw.WriterOpt.QueueMaxSize) + go ws.StartConsumer(i, ws.queues[i]) + } + + for i := 0; i < len(opts); i++ { + tlsConf, err := opts[i].ClientConfig.TLSConfig() + if err != nil { + return err + } + + trans := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: time.Duration(opts[i].DialTimeout) * time.Millisecond, + KeepAlive: time.Duration(opts[i].KeepAlive) * time.Millisecond, + }).DialContext, + ResponseHeaderTimeout: time.Duration(opts[i].Timeout) * time.Millisecond, + TLSHandshakeTimeout: time.Duration(opts[i].TLSHandshakeTimeout) * time.Millisecond, + ExpectContinueTimeout: time.Duration(opts[i].ExpectContinueTimeout) * time.Millisecond, + MaxConnsPerHost: opts[i].MaxConnsPerHost, + MaxIdleConns: opts[i].MaxIdleConns, + MaxIdleConnsPerHost: opts[i].MaxIdleConnsPerHost, + IdleConnTimeout: time.Duration(opts[i].IdleConnTimeout) * time.Millisecond, + } + + if tlsConf != nil { + trans.TLSClientConfig = tlsConf + } + + cli, err := api.NewClient(api.Config{ + Address: opts[i].Url, + RoundTripper: trans, + }) + + if err != nil { + return err + } + + writer := WriterType{ + Opts: opts[i], + Client: cli, + ForceUseServerTS: ws.pushgw.ForceUseServerTS, + } + + ws.Put(opts[i].Url, writer) + } + + return nil +} diff --git a/src/main.go b/src/main.go deleted file mode 100644 index 179905d0e47f099c9d6b984df0474588145a27b3..0000000000000000000000000000000000000000 --- a/src/main.go +++ /dev/null @@ -1,101 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/toolkits/pkg/runner" - "github.com/urfave/cli/v2" - - "github.com/didi/nightingale/v5/src/pkg/version" - "github.com/didi/nightingale/v5/src/server" - "github.com/didi/nightingale/v5/src/webapi" -) - -func main() { - app := cli.NewApp() - app.Name = "n9e" - app.Version = version.VERSION - app.Usage = "Nightingale, enterprise prometheus management" - app.Commands = []*cli.Command{ - newWebapiCmd(), - newServerCmd(), - } - app.Run(os.Args) -} - -func newWebapiCmd() *cli.Command { - return &cli.Command{ - Name: "webapi", - Usage: "Run webapi", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "conf", - Aliases: []string{"c"}, - Usage: "specify configuration file(.json,.yaml,.toml)", - }, - &cli.StringFlag{ - Name: "key", - Aliases: []string{"k"}, - Usage: "specify the secret key for configuration file field encryption", - }, - }, - Action: func(c *cli.Context) error { - printEnv() - - var opts []webapi.WebapiOption - if c.String("conf") != "" { - opts = append(opts, webapi.SetConfigFile(c.String("conf"))) - } - opts = append(opts, webapi.SetVersion(version.VERSION)) - if c.String("key") != "" { - opts = append(opts, webapi.SetKey(c.String("key"))) - } - - webapi.Run(opts...) - return nil - }, - } -} - -func newServerCmd() *cli.Command { - return &cli.Command{ - Name: "server", - Usage: "Run server", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "conf", - Aliases: []string{"c"}, - Usage: "specify configuration file(.json,.yaml,.toml)", - }, - &cli.StringFlag{ - Name: "key", - Aliases: []string{"k"}, - Usage: "specify the secret key for configuration file field encryption", - }, - }, - Action: func(c *cli.Context) error { - printEnv() - - var opts []server.ServerOption - if c.String("conf") != "" { - opts = append(opts, server.SetConfigFile(c.String("conf"))) - } - opts = append(opts, server.SetVersion(version.VERSION)) - if c.String("key") != "" { - opts = append(opts, server.SetKey(c.String("key"))) - } - - server.Run(opts...) - return nil - }, - } -} - -func printEnv() { - runner.Init() - fmt.Println("runner.cwd:", runner.Cwd) - fmt.Println("runner.hostname:", runner.Hostname) - fmt.Println("runner.fd_limits:", runner.FdLimits()) - fmt.Println("runner.vm_limits:", runner.VMLimits()) -} diff --git a/src/models/alert_his_event.go b/src/models/alert_his_event.go deleted file mode 100644 index 85af4e442c5a45bb425bc3f85947270233d7b549..0000000000000000000000000000000000000000 --- a/src/models/alert_his_event.go +++ /dev/null @@ -1,191 +0,0 @@ -package models - -import ( - "strconv" - "strings" -) - -type AlertHisEvent struct { - Id int64 `json:"id" gorm:"primaryKey"` - Cate string `json:"cate"` - IsRecovered int `json:"is_recovered"` - Cluster string `json:"cluster"` - GroupId int64 `json:"group_id"` - GroupName string `json:"group_name"` // busi group name - Hash string `json:"hash"` - RuleId int64 `json:"rule_id"` - RuleName string `json:"rule_name"` - RuleNote string `json:"rule_note"` - RuleProd string `json:"rule_prod"` - RuleAlgo string `json:"rule_algo"` - Severity int `json:"severity"` - PromForDuration int `json:"prom_for_duration"` - PromQl string `json:"prom_ql"` - PromEvalInterval int `json:"prom_eval_interval"` - Callbacks string `json:"-"` - CallbacksJSON []string `json:"callbacks" gorm:"-"` - RunbookUrl string `json:"runbook_url"` - NotifyRecovered int `json:"notify_recovered"` - NotifyChannels string `json:"-"` - NotifyChannelsJSON []string `json:"notify_channels" gorm:"-"` - NotifyGroups string `json:"-"` - NotifyGroupsJSON []string `json:"notify_groups" gorm:"-"` - NotifyGroupsObj []UserGroup `json:"notify_groups_obj" gorm:"-"` - TargetIdent string `json:"target_ident"` - TargetNote string `json:"target_note"` - TriggerTime int64 `json:"trigger_time"` - TriggerValue string `json:"trigger_value"` - RecoverTime int64 `json:"recover_time"` - LastEvalTime int64 `json:"last_eval_time"` - Tags string `json:"-"` - TagsJSON []string `json:"tags" gorm:"-"` - NotifyCurNumber int `json:"notify_cur_number"` // notify: current number - FirstTriggerTime int64 `json:"first_trigger_time"` // 连续告警的首次告警时间 -} - -func (e *AlertHisEvent) TableName() string { - return "alert_his_event" -} - -func (e *AlertHisEvent) Add() error { - return Insert(e) -} - -func (e *AlertHisEvent) DB2FE() { - e.NotifyChannelsJSON = strings.Fields(e.NotifyChannels) - e.NotifyGroupsJSON = strings.Fields(e.NotifyGroups) - e.CallbacksJSON = strings.Fields(e.Callbacks) - e.TagsJSON = strings.Split(e.Tags, ",,") -} - -func (e *AlertHisEvent) FillNotifyGroups(cache map[int64]*UserGroup) error { - // some user-group already deleted ? - count := len(e.NotifyGroupsJSON) - if count == 0 { - e.NotifyGroupsObj = []UserGroup{} - return nil - } - - for i := range e.NotifyGroupsJSON { - id, err := strconv.ParseInt(e.NotifyGroupsJSON[i], 10, 64) - if err != nil { - continue - } - - ug, has := cache[id] - if has { - e.NotifyGroupsObj = append(e.NotifyGroupsObj, *ug) - continue - } - - ug, err = UserGroupGetById(id) - if err != nil { - return err - } - - if ug != nil { - e.NotifyGroupsObj = append(e.NotifyGroupsObj, *ug) - cache[id] = ug - } - } - - return nil -} - -func AlertHisEventTotal(prod string, bgid, stime, etime int64, severity int, recovered int, clusters, cates []string, query string) (int64, error) { - session := DB().Model(&AlertHisEvent{}).Where("last_eval_time between ? and ? and rule_prod = ?", stime, etime, prod) - - if bgid > 0 { - session = session.Where("group_id = ?", bgid) - } - - if severity >= 0 { - session = session.Where("severity = ?", severity) - } - - if recovered >= 0 { - session = session.Where("is_recovered = ?", recovered) - } - - if len(clusters) > 0 { - session = session.Where("cluster in ?", clusters) - } - - if len(cates) > 0 { - session = session.Where("cate in ?", cates) - } - - if query != "" { - arr := strings.Fields(query) - for i := 0; i < len(arr); i++ { - qarg := "%" + arr[i] + "%" - session = session.Where("rule_name like ? or tags like ?", qarg, qarg) - } - } - - return Count(session) -} - -func AlertHisEventGets(prod string, bgid, stime, etime int64, severity int, recovered int, clusters, cates []string, query string, limit, offset int) ([]AlertHisEvent, error) { - session := DB().Where("last_eval_time between ? and ? and rule_prod = ?", stime, etime, prod) - - if bgid > 0 { - session = session.Where("group_id = ?", bgid) - } - - if severity >= 0 { - session = session.Where("severity = ?", severity) - } - - if recovered >= 0 { - session = session.Where("is_recovered = ?", recovered) - } - - if len(clusters) > 0 { - session = session.Where("cluster in ?", clusters) - } - - if len(cates) > 0 { - session = session.Where("cate in ?", cates) - } - - if query != "" { - arr := strings.Fields(query) - for i := 0; i < len(arr); i++ { - qarg := "%" + arr[i] + "%" - session = session.Where("rule_name like ? or tags like ?", qarg, qarg) - } - } - - var lst []AlertHisEvent - err := session.Order("id desc").Limit(limit).Offset(offset).Find(&lst).Error - - if err == nil { - for i := 0; i < len(lst); i++ { - lst[i].DB2FE() - } - } - - return lst, err -} - -func AlertHisEventGet(where string, args ...interface{}) (*AlertHisEvent, error) { - var lst []*AlertHisEvent - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - lst[0].DB2FE() - lst[0].FillNotifyGroups(make(map[int64]*UserGroup)) - - return lst[0], nil -} - -func AlertHisEventGetById(id int64) (*AlertHisEvent, error) { - return AlertHisEventGet("id=?", id) -} diff --git a/src/models/alert_mute.go b/src/models/alert_mute.go deleted file mode 100644 index c52e66d2a47608bf9c6bdae8c63043c8a98dfc9a..0000000000000000000000000000000000000000 --- a/src/models/alert_mute.go +++ /dev/null @@ -1,217 +0,0 @@ -package models - -import ( - "encoding/json" - "fmt" - "regexp" - "strings" - "time" - - "github.com/didi/nightingale/v5/src/pkg/ormx" - "github.com/pkg/errors" -) - -type TagFilter struct { - Key string `json:"key"` // tag key - Func string `json:"func"` // `==` | `=~` | `in` | `!=` | `!~` | `not in` - Value string `json:"value"` // tag value - Regexp *regexp.Regexp // parse value to regexp if func = '=~' or '!~' - Vset map[string]struct{} // parse value to regexp if func = 'in' or 'not in' -} - -type AlertMute struct { - Id int64 `json:"id" gorm:"primaryKey"` - GroupId int64 `json:"group_id"` - Note string `json:"note"` - Cate string `json:"cate"` - Prod string `json:"prod"` // product empty means n9e - Cluster string `json:"cluster"` // take effect by clusters, seperated by space - Tags ormx.JSONArr `json:"tags"` - Cause string `json:"cause"` - Btime int64 `json:"btime"` - Etime int64 `json:"etime"` - Disabled int `json:"disabled"` // 0: enabled, 1: disabled - CreateBy string `json:"create_by"` - UpdateBy string `json:"update_by"` - CreateAt int64 `json:"create_at"` - UpdateAt int64 `json:"update_at"` - ITags []TagFilter `json:"-" gorm:"-"` // inner tags -} - -func (m *AlertMute) TableName() string { - return "alert_mute" -} - -func AlertMuteGetById(id int64) (*AlertMute, error) { - return AlertMuteGet("id=?", id) -} - -func AlertMuteGet(where string, args ...interface{}) (*AlertMute, error) { - var lst []*AlertMute - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - return lst[0], nil -} - -func AlertMuteGets(prods []string, bgid int64, query string) (lst []AlertMute, err error) { - session := DB().Where("group_id = ? and prod in (?)", bgid, prods) - - if query != "" { - arr := strings.Fields(query) - for i := 0; i < len(arr); i++ { - qarg := "%" + arr[i] + "%" - session = session.Where("cause like ?", qarg) - } - } - - err = session.Order("id desc").Find(&lst).Error - return -} - -func AlertMuteGetsByBG(groupId int64) (lst []AlertMute, err error) { - err = DB().Where("group_id=?", groupId).Order("id desc").Find(&lst).Error - return -} - -func (m *AlertMute) Verify() error { - if m.GroupId < 0 { - return errors.New("group_id invalid") - } - - if m.Cluster == "" { - return errors.New("cluster invalid") - } - - if IsClusterAll(m.Cluster) { - m.Cluster = ClusterAll - } - - if m.Etime <= m.Btime { - return fmt.Errorf("oops... etime(%d) <= btime(%d)", m.Etime, m.Btime) - } - - if err := m.Parse(); err != nil { - return err - } - - if len(m.ITags) == 0 { - return errors.New("tags is blank") - } - - return nil -} - -func (m *AlertMute) Parse() error { - err := json.Unmarshal(m.Tags, &m.ITags) - if err != nil { - return err - } - - for i := 0; i < len(m.ITags); i++ { - if m.ITags[i].Func == "=~" || m.ITags[i].Func == "!~" { - m.ITags[i].Regexp, err = regexp.Compile(m.ITags[i].Value) - if err != nil { - return err - } - } else if m.ITags[i].Func == "in" || m.ITags[i].Func == "not in" { - arr := strings.Fields(m.ITags[i].Value) - m.ITags[i].Vset = make(map[string]struct{}) - for j := 0; j < len(arr); j++ { - m.ITags[i].Vset[arr[j]] = struct{}{} - } - } - } - - return nil -} - -func (m *AlertMute) Add() error { - if err := m.Verify(); err != nil { - return err - } - now := time.Now().Unix() - m.CreateAt = now - m.UpdateAt = now - return Insert(m) -} - -func (m *AlertMute) Update(arm AlertMute) error { - - arm.Id = m.Id - arm.GroupId = m.GroupId - arm.CreateAt = m.CreateAt - arm.CreateBy = m.CreateBy - arm.UpdateAt = time.Now().Unix() - - err := arm.Verify() - if err != nil { - return err - } - return DB().Model(m).Select("*").Updates(arm).Error -} - -func (m *AlertMute) UpdateFieldsMap(fields map[string]interface{}) error { - return DB().Model(m).Updates(fields).Error -} - -func AlertMuteDel(ids []int64) error { - if len(ids) == 0 { - return nil - } - return DB().Where("id in ?", ids).Delete(new(AlertMute)).Error -} - -func AlertMuteStatistics(cluster string) (*Statistics, error) { - // clean expired first - buf := int64(30) - err := DB().Where("etime < ?", time.Now().Unix()-buf).Delete(new(AlertMute)).Error - if err != nil { - return nil, err - } - - session := DB().Model(&AlertMute{}).Select("count(*) as total", "max(update_at) as last_updated") - if cluster != "" { - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var stats []*Statistics - err = session.Find(&stats).Error - if err != nil { - return nil, err - } - - return stats[0], nil -} - -func AlertMuteGetsByCluster(cluster string) ([]*AlertMute, error) { - // get my cluster's mutes - session := DB().Model(&AlertMute{}) - if cluster != "" { - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var lst []*AlertMute - var mlst []*AlertMute - err := session.Find(&lst).Error - if err != nil { - return nil, err - } - - if cluster == "" { - return lst, nil - } - - for _, m := range lst { - if MatchCluster(m.Cluster, cluster) { - mlst = append(mlst, m) - } - } - return mlst, err -} diff --git a/src/models/alert_rule.go b/src/models/alert_rule.go deleted file mode 100644 index 6bd5ed8d302ad80179ccb32c3b7af6244746aab4..0000000000000000000000000000000000000000 --- a/src/models/alert_rule.go +++ /dev/null @@ -1,504 +0,0 @@ -package models - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/webapi/config" -) - -type AlertRule struct { - Id int64 `json:"id" gorm:"primaryKey"` - GroupId int64 `json:"group_id"` // busi group id - Cate string `json:"cate"` // alert rule cate (prometheus|elasticsearch) - Cluster string `json:"cluster"` // take effect by clusters, seperated by space - Name string `json:"name"` // rule name - Note string `json:"note"` // will sent in notify - Prod string `json:"prod"` // product empty means n9e - Algorithm string `json:"algorithm"` // algorithm (''|holtwinters), empty means threshold - AlgoParams string `json:"-" gorm:"algo_params"` // params algorithm need - AlgoParamsJson interface{} `json:"algo_params" gorm:"-"` // - Delay int `json:"delay"` // Time (in seconds) to delay evaluation - Severity int `json:"severity"` // 1: Emergency 2: Warning 3: Notice - Disabled int `json:"disabled"` // 0: enabled, 1: disabled - PromForDuration int `json:"prom_for_duration"` // prometheus for, unit:s - PromQl string `json:"prom_ql"` // just one ql - PromEvalInterval int `json:"prom_eval_interval"` // unit:s - EnableStime string `json:"-"` // split by space: "00:00 10:00 12:00" - EnableStimeJSON string `json:"enable_stime" gorm:"-"` // for fe - EnableStimesJSON []string `json:"enable_stimes" gorm:"-"` // for fe - EnableEtime string `json:"-"` // split by space: "00:00 10:00 12:00" - EnableEtimeJSON string `json:"enable_etime" gorm:"-"` // for fe - EnableEtimesJSON []string `json:"enable_etimes" gorm:"-"` // for fe - EnableDaysOfWeek string `json:"-"` // eg: "0 1 2 3 4 5 6 ; 0 1 2" - EnableDaysOfWeekJSON []string `json:"enable_days_of_week" gorm:"-"` // for fe - EnableDaysOfWeeksJSON [][]string `json:"enable_days_of_weeks" gorm:"-"` // for fe - EnableInBG int `json:"enable_in_bg"` // 0: global 1: enable one busi-group - NotifyRecovered int `json:"notify_recovered"` // whether notify when recovery - NotifyChannels string `json:"-"` // split by space: sms voice email dingtalk wecom - NotifyChannelsJSON []string `json:"notify_channels" gorm:"-"` // for fe - NotifyGroups string `json:"-"` // split by space: 233 43 - NotifyGroupsObj []UserGroup `json:"notify_groups_obj" gorm:"-"` // for fe - NotifyGroupsJSON []string `json:"notify_groups" gorm:"-"` // for fe - NotifyRepeatStep int `json:"notify_repeat_step"` // notify repeat interval, unit: min - NotifyMaxNumber int `json:"notify_max_number"` // notify: max number - RecoverDuration int64 `json:"recover_duration"` // unit: s - Callbacks string `json:"-"` // split by space: http://a.com/api/x http://a.com/api/y' - CallbacksJSON []string `json:"callbacks" gorm:"-"` // for fe - RunbookUrl string `json:"runbook_url"` // sop url - AppendTags string `json:"-"` // split by space: service=n9e mod=api - AppendTagsJSON []string `json:"append_tags" gorm:"-"` // for fe - CreateAt int64 `json:"create_at"` - CreateBy string `json:"create_by"` - UpdateAt int64 `json:"update_at"` - UpdateBy string `json:"update_by"` -} - -func (ar *AlertRule) TableName() string { - return "alert_rule" -} - -func (ar *AlertRule) Verify() error { - if ar.GroupId < 0 { - return fmt.Errorf("GroupId(%d) invalid", ar.GroupId) - } - - if ar.Cluster == "" { - return errors.New("cluster is blank") - } - - if IsClusterAll(ar.Cluster) { - ar.Cluster = ClusterAll - } - - if str.Dangerous(ar.Name) { - return errors.New("Name has invalid characters") - } - - if ar.Name == "" { - return errors.New("name is blank") - } - - if ar.PromQl == "" { - return errors.New("prom_ql is blank") - } - - if ar.PromEvalInterval <= 0 { - ar.PromEvalInterval = 15 - } - - // check in front-end - // if _, err := parser.ParseExpr(ar.PromQl); err != nil { - // return errors.New("prom_ql parse error: %") - // } - - ar.AppendTags = strings.TrimSpace(ar.AppendTags) - arr := strings.Fields(ar.AppendTags) - for i := 0; i < len(arr); i++ { - if len(strings.Split(arr[i], "=")) != 2 { - return fmt.Errorf("AppendTags(%s) invalid", arr[i]) - } - } - - gids := strings.Fields(ar.NotifyGroups) - for i := 0; i < len(gids); i++ { - if _, err := strconv.ParseInt(gids[i], 10, 64); err != nil { - return fmt.Errorf("NotifyGroups(%s) invalid", ar.NotifyGroups) - } - } - - channels := strings.Fields(ar.NotifyChannels) - if len(channels) > 0 { - nlst := make([]string, 0, len(channels)) - for i := 0; i < len(channels); i++ { - if config.LabelAndKeyHasKey(config.C.NotifyChannels, channels[i]) { - nlst = append(nlst, channels[i]) - } - } - ar.NotifyChannels = strings.Join(nlst, " ") - } else { - ar.NotifyChannels = "" - } - - return nil -} - -func (ar *AlertRule) Add() error { - if err := ar.Verify(); err != nil { - return err - } - - exists, err := AlertRuleExists(0, ar.GroupId, ar.Cluster, ar.Name) - if err != nil { - return err - } - - if exists { - return errors.New("AlertRule already exists") - } - - now := time.Now().Unix() - ar.CreateAt = now - ar.UpdateAt = now - - return Insert(ar) -} - -func (ar *AlertRule) Update(arf AlertRule) error { - if ar.Name != arf.Name { - exists, err := AlertRuleExists(ar.Id, ar.GroupId, ar.Cluster, arf.Name) - if err != nil { - return err - } - - if exists { - return errors.New("AlertRule already exists") - } - } - - err := arf.FE2DB() - if err != nil { - return err - } - - arf.Id = ar.Id - arf.GroupId = ar.GroupId - arf.CreateAt = ar.CreateAt - arf.CreateBy = ar.CreateBy - arf.UpdateAt = time.Now().Unix() - - err = arf.Verify() - if err != nil { - return err - } - return DB().Model(ar).Select("*").Updates(arf).Error -} - -func (ar *AlertRule) UpdateFieldsMap(fields map[string]interface{}) error { - return DB().Model(ar).Updates(fields).Error -} - -func (ar *AlertRule) FillNotifyGroups(cache map[int64]*UserGroup) error { - // some user-group already deleted ? - count := len(ar.NotifyGroupsJSON) - if count == 0 { - ar.NotifyGroupsObj = []UserGroup{} - return nil - } - - exists := make([]string, 0, count) - delete := false - for i := range ar.NotifyGroupsJSON { - id, _ := strconv.ParseInt(ar.NotifyGroupsJSON[i], 10, 64) - - ug, has := cache[id] - if has { - exists = append(exists, ar.NotifyGroupsJSON[i]) - ar.NotifyGroupsObj = append(ar.NotifyGroupsObj, *ug) - continue - } - - ug, err := UserGroupGetById(id) - if err != nil { - return err - } - - if ug == nil { - delete = true - } else { - exists = append(exists, ar.NotifyGroupsJSON[i]) - ar.NotifyGroupsObj = append(ar.NotifyGroupsObj, *ug) - cache[id] = ug - } - } - - if delete { - // some user-group already deleted - ar.NotifyGroupsJSON = exists - ar.NotifyGroups = strings.Join(exists, " ") - DB().Model(ar).Update("notify_groups", ar.NotifyGroups) - } - - return nil -} - -func (ar *AlertRule) FE2DB() error { - if len(ar.EnableStimesJSON) > 0 { - ar.EnableStime = strings.Join(ar.EnableStimesJSON, " ") - ar.EnableEtime = strings.Join(ar.EnableEtimesJSON, " ") - } else { - ar.EnableStime = ar.EnableStimeJSON - ar.EnableEtime = ar.EnableEtimeJSON - } - - if len(ar.EnableDaysOfWeeksJSON) > 0 { - for i := 0; i < len(ar.EnableDaysOfWeeksJSON); i++ { - if len(ar.EnableDaysOfWeeksJSON) == 1 { - ar.EnableDaysOfWeek = strings.Join(ar.EnableDaysOfWeeksJSON[i], " ") - } else { - if i == len(ar.EnableDaysOfWeeksJSON)-1 { - ar.EnableDaysOfWeek += strings.Join(ar.EnableDaysOfWeeksJSON[i], " ") - } else { - ar.EnableDaysOfWeek += strings.Join(ar.EnableDaysOfWeeksJSON[i], " ") + ";" - } - } - } - } else { - ar.EnableDaysOfWeek = strings.Join(ar.EnableDaysOfWeekJSON, " ") - } - - ar.NotifyChannels = strings.Join(ar.NotifyChannelsJSON, " ") - ar.NotifyGroups = strings.Join(ar.NotifyGroupsJSON, " ") - ar.Callbacks = strings.Join(ar.CallbacksJSON, " ") - ar.AppendTags = strings.Join(ar.AppendTagsJSON, " ") - algoParamsByte, err := json.Marshal(ar.AlgoParamsJson) - if err != nil { - return fmt.Errorf("marshal algo_params err:%v", err) - } - - ar.AlgoParams = string(algoParamsByte) - return nil -} - -func (ar *AlertRule) DB2FE() { - ar.EnableStimesJSON = strings.Fields(ar.EnableStime) - ar.EnableEtimesJSON = strings.Fields(ar.EnableEtime) - if len(ar.EnableEtimesJSON) > 0 { - ar.EnableStimeJSON = ar.EnableStimesJSON[0] - ar.EnableEtimeJSON = ar.EnableEtimesJSON[0] - } - - cache := strings.Split(ar.EnableDaysOfWeek, ";") - for i := 0; i < len(cache); i++ { - ar.EnableDaysOfWeeksJSON = append(ar.EnableDaysOfWeeksJSON, strings.Fields(cache[i])) - } - if len(ar.EnableDaysOfWeeksJSON) > 0 { - ar.EnableDaysOfWeekJSON = ar.EnableDaysOfWeeksJSON[0] - } - - ar.NotifyChannelsJSON = strings.Fields(ar.NotifyChannels) - ar.NotifyGroupsJSON = strings.Fields(ar.NotifyGroups) - ar.CallbacksJSON = strings.Fields(ar.Callbacks) - ar.AppendTagsJSON = strings.Fields(ar.AppendTags) - json.Unmarshal([]byte(ar.AlgoParams), &ar.AlgoParamsJson) -} - -func AlertRuleDels(ids []int64, bgid ...int64) error { - for i := 0; i < len(ids); i++ { - session := DB().Where("id = ?", ids[i]) - if len(bgid) > 0 { - session = session.Where("group_id = ?", bgid[0]) - } - ret := session.Delete(&AlertRule{}) - if ret.Error != nil { - return ret.Error - } - - // 说明确实删掉了,把相关的活跃告警也删了,这些告警永远都不会恢复了,而且策略都没了,说明没人关心了 - if ret.RowsAffected > 0 { - DB().Where("rule_id = ?", ids[i]).Delete(new(AlertCurEvent)) - } - } - - return nil -} - -func AlertRuleExists(id, groupId int64, cluster, name string) (bool, error) { - session := DB().Where("id <> ? and group_id = ? and name = ?", id, groupId, name) - - var lst []AlertRule - err := session.Find(&lst).Error - if err != nil { - return false, err - } - if len(lst) == 0 { - return false, nil - } - - // match cluster - for _, r := range lst { - if MatchCluster(r.Cluster, cluster) { - return true, nil - } - } - return false, nil -} - -func AlertRuleGets(groupId int64) ([]AlertRule, error) { - session := DB().Where("group_id=?", groupId).Order("name") - - var lst []AlertRule - err := session.Find(&lst).Error - if err == nil { - for i := 0; i < len(lst); i++ { - lst[i].DB2FE() - } - } - - return lst, err -} - -func AlertRuleGetsByCluster(cluster string) ([]*AlertRule, error) { - session := DB().Where("disabled = ? and prod = ?", 0, "") - - if cluster != "" { - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var lst []*AlertRule - err := session.Find(&lst).Error - if err != nil { - return lst, err - } - - if len(lst) == 0 { - return lst, nil - } - - if cluster == "" { - for i := 0; i < len(lst); i++ { - lst[i].DB2FE() - } - return lst, nil - } - - lr := make([]*AlertRule, 0, len(lst)) - for _, r := range lst { - if MatchCluster(r.Cluster, cluster) { - r.DB2FE() - lr = append(lr, r) - } - } - - return lr, err -} - -func AlertRulesGetsBy(prods []string, query, algorithm, cluster string, cates []string, disabled int) ([]*AlertRule, error) { - session := DB().Where("prod in (?)", prods) - - if query != "" { - arr := strings.Fields(query) - for i := 0; i < len(arr); i++ { - qarg := "%" + arr[i] + "%" - session = session.Where("append_tags like ?", qarg) - } - } - - if algorithm != "" { - session = session.Where("algorithm = ?", algorithm) - } - - if cluster != "" { - session = session.Where("cluster like ?", "%"+cluster+"%") - } - - if len(cates) != 0 { - session = session.Where("cate in (?)", cates) - } - - if disabled != -1 { - session = session.Where("disabled = ?", disabled) - } - - var lst []*AlertRule - err := session.Find(&lst).Error - if err == nil { - for i := 0; i < len(lst); i++ { - lst[i].DB2FE() - } - } - - return lst, err -} - -func AlertRuleGet(where string, args ...interface{}) (*AlertRule, error) { - var lst []*AlertRule - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - lst[0].DB2FE() - - return lst[0], nil -} - -func AlertRuleGetById(id int64) (*AlertRule, error) { - return AlertRuleGet("id=?", id) -} - -func AlertRuleGetName(id int64) (string, error) { - var names []string - err := DB().Model(new(AlertRule)).Where("id = ?", id).Pluck("name", &names).Error - if err != nil { - return "", err - } - - if len(names) == 0 { - return "", nil - } - - return names[0], nil -} - -func AlertRuleStatistics(cluster string) (*Statistics, error) { - session := DB().Model(&AlertRule{}).Select("count(*) as total", "max(update_at) as last_updated").Where("disabled = ? and prod = ?", 0, "") - - if cluster != "" { - // 简略的判断,当一个clustername是另一个clustername的substring的时候,会出现stats与预期不符,不影响使用 - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var stats []*Statistics - err := session.Find(&stats).Error - if err != nil { - return nil, err - } - - return stats[0], nil -} - -func (ar *AlertRule) IsPrometheusRule() bool { - return ar.Algorithm == "" && (ar.Cate == "" || strings.ToLower(ar.Cate) == "prometheus") -} - -func (ar *AlertRule) GenerateNewEvent() *AlertCurEvent { - event := &AlertCurEvent{} - ar.UpdateEvent(event) - return event -} - -func (ar *AlertRule) UpdateEvent(event *AlertCurEvent) { - if event == nil { - return - } - event.GroupId = ar.GroupId - event.Cate = ar.Cate - event.RuleId = ar.Id - event.RuleName = ar.Name - event.RuleNote = ar.Note - event.RuleProd = ar.Prod - event.RuleAlgo = ar.Algorithm - event.Severity = ar.Severity - event.PromForDuration = ar.PromForDuration - event.PromQl = ar.PromQl - event.PromEvalInterval = ar.PromEvalInterval - event.Callbacks = ar.Callbacks - event.CallbacksJSON = ar.CallbacksJSON - event.RunbookUrl = ar.RunbookUrl - event.NotifyRecovered = ar.NotifyRecovered - event.NotifyChannels = ar.NotifyChannels - event.NotifyChannelsJSON = ar.NotifyChannelsJSON - event.NotifyGroups = ar.NotifyGroups - event.NotifyGroupsJSON = ar.NotifyGroupsJSON -} diff --git a/src/models/alert_subscribe.go b/src/models/alert_subscribe.go deleted file mode 100644 index f02f1f4a0920b8a851e3969fe3272b40f1571e13..0000000000000000000000000000000000000000 --- a/src/models/alert_subscribe.go +++ /dev/null @@ -1,279 +0,0 @@ -package models - -import ( - "encoding/json" - "regexp" - "strconv" - "strings" - "time" - - "github.com/didi/nightingale/v5/src/pkg/ormx" - "github.com/pkg/errors" -) - -type AlertSubscribe struct { - Id int64 `json:"id" gorm:"primaryKey"` - Name string `json:"name"` // AlertSubscribe name - Disabled int `json:"disabled"` // 0: enabled, 1: disabled - GroupId int64 `json:"group_id"` - Cate string `json:"cate"` - Cluster string `json:"cluster"` // take effect by clusters, seperated by space - RuleId int64 `json:"rule_id"` - RuleName string `json:"rule_name" gorm:"-"` // for fe - Tags ormx.JSONArr `json:"tags"` - RedefineSeverity int `json:"redefine_severity"` - NewSeverity int `json:"new_severity"` - RedefineChannels int `json:"redefine_channels"` - NewChannels string `json:"new_channels"` - UserGroupIds string `json:"user_group_ids"` - UserGroups []UserGroup `json:"user_groups" gorm:"-"` // for fe - CreateBy string `json:"create_by"` - CreateAt int64 `json:"create_at"` - UpdateBy string `json:"update_by"` - UpdateAt int64 `json:"update_at"` - ITags []TagFilter `json:"-" gorm:"-"` // inner tags -} - -func (s *AlertSubscribe) TableName() string { - return "alert_subscribe" -} - -func AlertSubscribeGets(groupId int64) (lst []AlertSubscribe, err error) { - err = DB().Where("group_id=?", groupId).Order("id desc").Find(&lst).Error - return -} - -func AlertSubscribeGet(where string, args ...interface{}) (*AlertSubscribe, error) { - var lst []*AlertSubscribe - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - return lst[0], nil -} - -func (s *AlertSubscribe) IsDisabled() bool { - return s.Disabled == 1 -} - -func (s *AlertSubscribe) Verify() error { - if s.Cluster == "" { - return errors.New("cluster invalid") - } - - if IsClusterAll(s.Cluster) { - s.Cluster = ClusterAll - } - - if err := s.Parse(); err != nil { - return err - } - - if len(s.ITags) == 0 && s.RuleId == 0 { - return errors.New("rule_id and tags are both blank") - } - - ugids := strings.Fields(s.UserGroupIds) - for i := 0; i < len(ugids); i++ { - if _, err := strconv.ParseInt(ugids[i], 10, 64); err != nil { - return errors.New("user_group_ids invalid") - } - } - - return nil -} - -func (s *AlertSubscribe) Parse() error { - err := json.Unmarshal(s.Tags, &s.ITags) - if err != nil { - return err - } - - for i := 0; i < len(s.ITags); i++ { - if s.ITags[i].Func == "=~" || s.ITags[i].Func == "!~" { - s.ITags[i].Regexp, err = regexp.Compile(s.ITags[i].Value) - if err != nil { - return err - } - } else if s.ITags[i].Func == "in" || s.ITags[i].Func == "not in" { - arr := strings.Fields(s.ITags[i].Value) - s.ITags[i].Vset = make(map[string]struct{}) - for j := 0; j < len(arr); j++ { - s.ITags[i].Vset[arr[j]] = struct{}{} - } - } - } - - return nil -} - -func (s *AlertSubscribe) Add() error { - if err := s.Verify(); err != nil { - return err - } - - now := time.Now().Unix() - s.CreateAt = now - s.UpdateAt = now - return Insert(s) -} - -func (s *AlertSubscribe) FillRuleName(cache map[int64]string) error { - if s.RuleId <= 0 { - s.RuleName = "" - return nil - } - - name, has := cache[s.RuleId] - if has { - s.RuleName = name - return nil - } - - name, err := AlertRuleGetName(s.RuleId) - if err != nil { - return err - } - - if name == "" { - name = "Error: AlertRule not found" - } - - s.RuleName = name - cache[s.RuleId] = name - return nil -} - -func (s *AlertSubscribe) FillUserGroups(cache map[int64]*UserGroup) error { - // some user-group already deleted ? - ugids := strings.Fields(s.UserGroupIds) - - count := len(ugids) - if count == 0 { - s.UserGroups = []UserGroup{} - return nil - } - - exists := make([]string, 0, count) - delete := false - for i := range ugids { - id, _ := strconv.ParseInt(ugids[i], 10, 64) - - ug, has := cache[id] - if has { - exists = append(exists, ugids[i]) - s.UserGroups = append(s.UserGroups, *ug) - continue - } - - ug, err := UserGroupGetById(id) - if err != nil { - return err - } - - if ug == nil { - delete = true - } else { - exists = append(exists, ugids[i]) - s.UserGroups = append(s.UserGroups, *ug) - cache[id] = ug - } - } - - if delete { - // some user-group already deleted - DB().Model(s).Update("user_group_ids", strings.Join(exists, " ")) - s.UserGroupIds = strings.Join(exists, " ") - } - - return nil -} - -func (s *AlertSubscribe) Update(selectField interface{}, selectFields ...interface{}) error { - if err := s.Verify(); err != nil { - return err - } - - return DB().Model(s).Select(selectField, selectFields...).Updates(s).Error -} - -func AlertSubscribeDel(ids []int64) error { - if len(ids) == 0 { - return nil - } - return DB().Where("id in ?", ids).Delete(new(AlertSubscribe)).Error -} - -func AlertSubscribeStatistics(cluster string) (*Statistics, error) { - session := DB().Model(&AlertSubscribe{}).Select("count(*) as total", "max(update_at) as last_updated") - - if cluster != "" { - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var stats []*Statistics - err := session.Find(&stats).Error - if err != nil { - return nil, err - } - - return stats[0], nil -} - -func AlertSubscribeGetsByCluster(cluster string) ([]*AlertSubscribe, error) { - // get my cluster's subscribes - session := DB().Model(&AlertSubscribe{}) - if cluster != "" { - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var lst []*AlertSubscribe - var slst []*AlertSubscribe - err := session.Find(&lst).Error - if err != nil { - return nil, err - } - - if cluster == "" { - return lst, nil - } - - for _, s := range lst { - if MatchCluster(s.Cluster, cluster) { - slst = append(slst, s) - } - } - return slst, err -} - -func (s *AlertSubscribe) MatchCluster(cluster string) bool { - if s.Cluster == ClusterAll { - return true - } - clusters := strings.Fields(s.Cluster) - for _, c := range clusters { - if c == cluster { - return true - } - } - return false -} - -func (s *AlertSubscribe) ModifyEvent(event *AlertCurEvent) { - if s.RedefineSeverity == 1 { - event.Severity = s.NewSeverity - } - - if s.RedefineChannels == 1 { - event.NotifyChannels = s.NewChannels - event.NotifyChannelsJSON = strings.Fields(s.NewChannels) - } - - event.NotifyGroups = s.UserGroupIds - event.NotifyGroupsJSON = strings.Fields(s.UserGroupIds) -} diff --git a/src/models/alerting_engine.go b/src/models/alerting_engine.go deleted file mode 100644 index b14f2cdf150ee9fd96f2bd617fe40e7575df001e..0000000000000000000000000000000000000000 --- a/src/models/alerting_engine.go +++ /dev/null @@ -1,144 +0,0 @@ -package models - -import ( - "fmt" - "time" -) - -type AlertingEngines struct { - Id int64 `json:"id" gorm:"primaryKey"` - Instance string `json:"instance"` - Cluster string `json:"cluster"` // reader cluster - Clock int64 `json:"clock"` -} - -func (e *AlertingEngines) TableName() string { - return "alerting_engines" -} - -// UpdateCluster 页面上用户会给各个n9e-server分配要关联的目标集群是什么 -func (e *AlertingEngines) UpdateCluster(c string) error { - count, err := Count(DB().Model(&AlertingEngines{}).Where("id<>? and instance=? and cluster=?", e.Id, e.Instance, c)) - if err != nil { - return err - } - - if count > 0 { - return fmt.Errorf("instance %s and cluster %s already exists", e.Instance, c) - } - - e.Cluster = c - return DB().Model(e).Select("cluster").Updates(e).Error -} - -func AlertingEngineAdd(instance, cluster string) error { - count, err := Count(DB().Model(&AlertingEngines{}).Where("instance=? and cluster=?", instance, cluster)) - if err != nil { - return err - } - - if count > 0 { - return fmt.Errorf("instance %s and cluster %s already exists", instance, cluster) - } - - err = DB().Create(&AlertingEngines{ - Instance: instance, - Cluster: cluster, - Clock: time.Now().Unix(), - }).Error - - return err -} - -func AlertingEngineDel(ids []int64) error { - if len(ids) == 0 { - return nil - } - return DB().Where("id in ?", ids).Delete(new(AlertingEngines)).Error -} - -// AlertingEngineGetCluster 根据实例名获取对应的集群名字 -func AlertingEngineGetClusters(instance string) ([]string, error) { - var objs []AlertingEngines - err := DB().Where("instance=?", instance).Find(&objs).Error - if err != nil { - return []string{}, err - } - - if len(objs) == 0 { - return []string{}, nil - } - var clusters []string - for i := 0; i < len(objs); i++ { - clusters = append(clusters, objs[i].Cluster) - } - - return clusters, nil -} - -// AlertingEngineGets 拉取列表数据,用户要在页面上看到所有 n9e-server 实例列表,然后为其分配 cluster -func AlertingEngineGets(where string, args ...interface{}) ([]*AlertingEngines, error) { - var objs []*AlertingEngines - var err error - session := DB().Order("instance") - if where == "" { - err = session.Find(&objs).Error - } else { - err = session.Where(where, args...).Find(&objs).Error - } - return objs, err -} - -func AlertingEngineGet(where string, args ...interface{}) (*AlertingEngines, error) { - lst, err := AlertingEngineGets(where, args...) - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - return lst[0], nil -} - -func AlertingEngineGetsInstances(where string, args ...interface{}) ([]string, error) { - var arr []string - var err error - session := DB().Model(new(AlertingEngines)).Order("instance") - if where == "" { - err = session.Pluck("instance", &arr).Error - } else { - err = session.Where(where, args...).Pluck("instance", &arr).Error - } - return arr, err -} - -func AlertingEngineHeartbeatWithCluster(instance, cluster string) error { - var total int64 - err := DB().Model(new(AlertingEngines)).Where("instance=? and cluster=?", instance, cluster).Count(&total).Error - if err != nil { - return err - } - - if total == 0 { - // insert - err = DB().Create(&AlertingEngines{ - Instance: instance, - Cluster: cluster, - Clock: time.Now().Unix(), - }).Error - } else { - // updates - fields := map[string]interface{}{"clock": time.Now().Unix()} - err = DB().Model(new(AlertingEngines)).Where("instance=? and cluster=?", instance, cluster).Updates(fields).Error - } - - return err -} - -func AlertingEngineHeartbeat(instance string) error { - fields := map[string]interface{}{"clock": time.Now().Unix()} - err := DB().Model(new(AlertingEngines)).Where("instance=?", instance).Updates(fields).Error - return err -} diff --git a/src/models/board.go b/src/models/board.go deleted file mode 100644 index a6dfd4e6d1d39f0ff80b7c8ec1903593e105e147..0000000000000000000000000000000000000000 --- a/src/models/board.go +++ /dev/null @@ -1,166 +0,0 @@ -package models - -import ( - "strings" - "time" - - "github.com/pkg/errors" - "github.com/toolkits/pkg/str" - "gorm.io/gorm" -) - -type Board struct { - Id int64 `json:"id" gorm:"primaryKey"` - GroupId int64 `json:"group_id"` - Name string `json:"name"` - Ident string `json:"ident"` - Tags string `json:"tags"` - CreateAt int64 `json:"create_at"` - CreateBy string `json:"create_by"` - UpdateAt int64 `json:"update_at"` - UpdateBy string `json:"update_by"` - Configs string `json:"configs" gorm:"-"` - Public int `json:"public"` // 0: false, 1: true -} - -func (b *Board) TableName() string { - return "board" -} - -func (b *Board) Verify() error { - if b.Name == "" { - return errors.New("Name is blank") - } - - if str.Dangerous(b.Name) { - return errors.New("Name has invalid characters") - } - - return nil -} - -func (b *Board) CanRenameIdent(ident string) (bool, error) { - if ident == "" { - return true, nil - } - - cnt, err := Count(DB().Model(b).Where("ident=? and id <> ?", ident, b.Id)) - if err != nil { - return false, err - } - - return cnt == 0, nil -} - -func (b *Board) Add() error { - if err := b.Verify(); err != nil { - return err - } - - if b.Ident != "" { - // ident duplicate check - cnt, err := Count(DB().Model(b).Where("ident=?", b.Ident)) - if err != nil { - return err - } - - if cnt > 0 { - return errors.New("Ident duplicate") - } - } - - now := time.Now().Unix() - b.CreateAt = now - b.UpdateAt = now - - return Insert(b) -} - -func (b *Board) Update(selectField interface{}, selectFields ...interface{}) error { - if err := b.Verify(); err != nil { - return err - } - - return DB().Model(b).Select(selectField, selectFields...).Updates(b).Error -} - -func (b *Board) Del() error { - return DB().Transaction(func(tx *gorm.DB) error { - if err := tx.Where("id=?", b.Id).Delete(&BoardPayload{}).Error; err != nil { - return err - } - - if err := tx.Where("id=?", b.Id).Delete(&Board{}).Error; err != nil { - return err - } - - return nil - }) -} - -func BoardGetByID(id int64) (*Board, error) { - var lst []*Board - err := DB().Where("id = ?", id).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - return lst[0], nil -} - -// BoardGet for detail page -func BoardGet(where string, args ...interface{}) (*Board, error) { - var lst []*Board - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - payload, err := BoardPayloadGet(lst[0].Id) - if err != nil { - return nil, err - } - - lst[0].Configs = payload - - return lst[0], nil -} - -func BoardCount(where string, args ...interface{}) (num int64, err error) { - return Count(DB().Model(&Board{}).Where(where, args...)) -} - -func BoardExists(where string, args ...interface{}) (bool, error) { - num, err := BoardCount(where, args...) - return num > 0, err -} - -// BoardGets for list page -func BoardGets(groupId int64, query string) ([]Board, error) { - session := DB().Where("group_id=?", groupId).Order("name") - - arr := strings.Fields(query) - if len(arr) > 0 { - for i := 0; i < len(arr); i++ { - if strings.HasPrefix(arr[i], "-") { - q := "%" + arr[i][1:] + "%" - session = session.Where("name not like ? and tags not like ?", q, q) - } else { - q := "%" + arr[i] + "%" - session = session.Where("(name like ? or tags like ?)", q, q) - } - } - } - - var objs []Board - err := session.Find(&objs).Error - return objs, err -} diff --git a/src/models/board_payload.go b/src/models/board_payload.go deleted file mode 100644 index a988227ca35a506b1328ef0cf1cc395165d36d2c..0000000000000000000000000000000000000000 --- a/src/models/board_payload.go +++ /dev/null @@ -1,58 +0,0 @@ -package models - -import "errors" - -type BoardPayload struct { - Id int64 `json:"id" gorm:"primaryKey"` - Payload string `json:"payload"` -} - -func (p *BoardPayload) TableName() string { - return "board_payload" -} - -func (p *BoardPayload) Update(selectField interface{}, selectFields ...interface{}) error { - return DB().Model(p).Select(selectField, selectFields...).Updates(p).Error -} - -func BoardPayloadGets(ids []int64) ([]*BoardPayload, error) { - if len(ids) == 0 { - return nil, errors.New("empty ids") - } - - var arr []*BoardPayload - err := DB().Where("id in ?", ids).Find(&arr).Error - return arr, err -} - -func BoardPayloadGet(id int64) (string, error) { - payloads, err := BoardPayloadGets([]int64{id}) - if err != nil { - return "", err - } - - if len(payloads) == 0 { - return "", nil - } - - return payloads[0].Payload, nil -} - -func BoardPayloadSave(id int64, payload string) error { - var bp BoardPayload - err := DB().Where("id = ?", id).Find(&bp).Error - if err != nil { - return err - } - - if bp.Id > 0 { - // already exists - bp.Payload = payload - return bp.Update("payload") - } - - return Insert(&BoardPayload{ - Id: id, - Payload: payload, - }) -} diff --git a/src/models/busi_group_member.go b/src/models/busi_group_member.go deleted file mode 100644 index 0d4b1d3cf1d78ddd904ac221984301f10a8d80ce..0000000000000000000000000000000000000000 --- a/src/models/busi_group_member.go +++ /dev/null @@ -1,92 +0,0 @@ -package models - -type BusiGroupMember struct { - BusiGroupId int64 `json:"busi_group_id"` - UserGroupId int64 `json:"user_group_id"` - PermFlag string `json:"perm_flag"` -} - -func (BusiGroupMember) TableName() string { - return "busi_group_member" -} - -func BusiGroupIds(userGroupIds []int64, permFlag ...string) ([]int64, error) { - if len(userGroupIds) == 0 { - return []int64{}, nil - } - - session := DB().Model(&BusiGroupMember{}).Where("user_group_id in ?", userGroupIds) - if len(permFlag) > 0 { - session = session.Where("perm_flag=?", permFlag[0]) - } - - var ids []int64 - err := session.Pluck("busi_group_id", &ids).Error - return ids, err -} - -func UserGroupIdsOfBusiGroup(busiGroupId int64, permFlag ...string) ([]int64, error) { - session := DB().Model(&BusiGroupMember{}).Where("busi_group_id = ?", busiGroupId) - if len(permFlag) > 0 { - session = session.Where("perm_flag=?", permFlag[0]) - } - - var ids []int64 - err := session.Pluck("user_group_id", &ids).Error - return ids, err -} - -func BusiGroupMemberCount(where string, args ...interface{}) (int64, error) { - return Count(DB().Model(&BusiGroupMember{}).Where(where, args...)) -} - -func BusiGroupMemberAdd(member BusiGroupMember) error { - obj, err := BusiGroupMemberGet("busi_group_id = ? and user_group_id = ?", member.BusiGroupId, member.UserGroupId) - if err != nil { - return err - } - - if obj == nil { - // insert - return Insert(&BusiGroupMember{ - BusiGroupId: member.BusiGroupId, - UserGroupId: member.UserGroupId, - PermFlag: member.PermFlag, - }) - } else { - // update - if obj.PermFlag == member.PermFlag { - return nil - } - - return DB().Model(&BusiGroupMember{}).Where("busi_group_id = ? and user_group_id = ?", member.BusiGroupId, member.UserGroupId).Update("perm_flag", member.PermFlag).Error - } -} - -func BusiGroupMemberGet(where string, args ...interface{}) (*BusiGroupMember, error) { - var lst []*BusiGroupMember - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - return lst[0], nil -} - -func BusiGroupMemberDel(where string, args ...interface{}) error { - return DB().Where(where, args...).Delete(&BusiGroupMember{}).Error -} - -func BusiGroupMemberGets(where string, args ...interface{}) ([]BusiGroupMember, error) { - var lst []BusiGroupMember - err := DB().Where(where, args...).Order("perm_flag").Find(&lst).Error - return lst, err -} - -func BusiGroupMemberGetsByBusiGroupId(busiGroupId int64) ([]BusiGroupMember, error) { - return BusiGroupMemberGets("busi_group_id=?", busiGroupId) -} diff --git a/src/models/chart.go b/src/models/chart.go deleted file mode 100644 index 3dbf9dfac10dfa12ece0bc7cfca520912300bdbf..0000000000000000000000000000000000000000 --- a/src/models/chart.go +++ /dev/null @@ -1,30 +0,0 @@ -package models - -type Chart struct { - Id int64 `json:"id" gorm:"primaryKey"` - GroupId int64 `json:"group_id"` - Configs string `json:"configs"` - Weight int `json:"weight"` -} - -func (c *Chart) TableName() string { - return "chart" -} - -func ChartsOf(chartGroupId int64) ([]Chart, error) { - var objs []Chart - err := DB().Where("group_id = ?", chartGroupId).Order("weight").Find(&objs).Error - return objs, err -} - -func (c *Chart) Add() error { - return Insert(c) -} - -func (c *Chart) Update(selectField interface{}, selectFields ...interface{}) error { - return DB().Model(c).Select(selectField, selectFields...).Updates(c).Error -} - -func (c *Chart) Del() error { - return DB().Where("id=?", c.Id).Delete(&Chart{}).Error -} diff --git a/src/models/chart_share.go b/src/models/chart_share.go deleted file mode 100644 index 69dc002ba491fd388d274fd7a74353f14b12cc50..0000000000000000000000000000000000000000 --- a/src/models/chart_share.go +++ /dev/null @@ -1,27 +0,0 @@ -package models - -type ChartShare struct { - Id int64 `json:"id" gorm:"primaryKey"` - Cluster string `json:"cluster"` - Configs string `json:"configs"` - CreateBy string `json:"create_by"` - CreateAt int64 `json:"create_at"` -} - -func (cs *ChartShare) TableName() string { - return "chart_share" -} - -func (cs *ChartShare) Add() error { - return Insert(cs) -} - -func ChartShareGetsByIds(ids []int64) ([]ChartShare, error) { - var lst []ChartShare - if len(ids) == 0 { - return lst, nil - } - - err := DB().Where("id in ?", ids).Order("id").Find(&lst).Error - return lst, err -} diff --git a/src/models/common.go b/src/models/common.go deleted file mode 100644 index a6c6d438e0807af6a07cfdcc71945c62a3dab07b..0000000000000000000000000000000000000000 --- a/src/models/common.go +++ /dev/null @@ -1,72 +0,0 @@ -package models - -import ( - "strings" - - "github.com/toolkits/pkg/str" - "gorm.io/gorm" - - "github.com/didi/nightingale/v5/src/storage" -) - -const AdminRole = "Admin" - -// if rule's cluster field contains `ClusterAll`, means it take effect in all clusters -const ClusterAll = "$all" - -func DB() *gorm.DB { - return storage.DB -} - -func Count(tx *gorm.DB) (int64, error) { - var cnt int64 - err := tx.Count(&cnt).Error - return cnt, err -} - -func Exists(tx *gorm.DB) (bool, error) { - num, err := Count(tx) - return num > 0, err -} - -func Insert(obj interface{}) error { - return DB().Create(obj).Error -} - -// CryptoPass crypto password use salt -func CryptoPass(raw string) (string, error) { - salt, err := ConfigsGet("salt") - if err != nil { - return "", err - } - - return str.MD5(salt + "<-*Uk30^96eY*->" + raw), nil -} - -type Statistics struct { - Total int64 `gorm:"total"` - LastUpdated int64 `gorm:"last_updated"` -} - -func MatchCluster(ruleCluster, targetCluster string) bool { - if targetCluster == ClusterAll { - return true - } - clusters := strings.Fields(ruleCluster) - for _, c := range clusters { - if c == ClusterAll || c == targetCluster { - return true - } - } - return false -} - -func IsClusterAll(ruleCluster string) bool { - clusters := strings.Fields(ruleCluster) - for _, c := range clusters { - if c == ClusterAll { - return true - } - } - return false -} diff --git a/src/models/metric_description.go b/src/models/metric_description.go deleted file mode 100644 index 7dd13e25f2a0692dfc5fbd8189eaed7985121ca4..0000000000000000000000000000000000000000 --- a/src/models/metric_description.go +++ /dev/null @@ -1,138 +0,0 @@ -package models - -import ( - "strings" - "time" -) - -type MetricDescription struct { - Id int64 `json:"id"` - Metric string `json:"metric"` - Description string `json:"description"` - UpdateAt int64 `json:"update_at"` -} - -func (md *MetricDescription) TableName() string { - return "metric_description" -} - -func MetricDescriptionUpdate(mds []MetricDescription) error { - now := time.Now().Unix() - - for i := 0; i < len(mds); i++ { - mds[i].Metric = strings.TrimSpace(mds[i].Metric) - md, err := MetricDescriptionGet("metric = ?", mds[i].Metric) - if err != nil { - return err - } - - if md == nil { - // insert - mds[i].UpdateAt = now - err = Insert(&mds[i]) - if err != nil { - return err - } - } else { - // update - err = md.Update(mds[i].Description, now) - if err != nil { - return err - } - } - } - return nil -} - -func (md *MetricDescription) Update(desn string, now int64) error { - md.Description = desn - md.UpdateAt = now - return DB().Model(md).Select("Description", "UpdateAt").Updates(md).Error -} - -func MetricDescriptionGet(where string, args ...interface{}) (*MetricDescription, error) { - var lst []*MetricDescription - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - return lst[0], nil -} - -func MetricDescriptionTotal(query string) (int64, error) { - session := DB().Model(&MetricDescription{}) - - if query != "" { - q := "%" + query + "%" - session = session.Where("metric like ? or description like ?", q, q) - } - - return Count(session) -} - -func MetricDescriptionGets(query string, limit, offset int) ([]MetricDescription, error) { - session := DB().Order("metric").Limit(limit).Offset(offset) - if query != "" { - q := "%" + query + "%" - session = session.Where("metric like ? or description like ?", q, q) - } - - var objs []MetricDescription - err := session.Find(&objs).Error - return objs, err -} - -func MetricDescGetAll() ([]MetricDescription, error) { - var objs []MetricDescription - err := DB().Find(&objs).Error - return objs, err -} - -func MetricDescStatistics() (*Statistics, error) { - session := DB().Model(&MetricDescription{}).Select("count(*) as total", "max(update_at) as last_updated") - - var stats []*Statistics - err := session.Find(&stats).Error - if err != nil { - return nil, err - } - - return stats[0], nil -} - -func MetricDescriptionMapper(metrics []string) (map[string]string, error) { - if len(metrics) == 0 { - return map[string]string{}, nil - } - - var objs []MetricDescription - err := DB().Where("metric in ?", metrics).Find(&objs).Error - if err != nil { - return nil, err - } - - count := len(objs) - if count == 0 { - return map[string]string{}, nil - } - - mapper := make(map[string]string, count) - for i := 0; i < count; i++ { - mapper[objs[i].Metric] = objs[i].Description - } - - return mapper, nil -} - -func MetricDescriptionDel(ids []int64) error { - if len(ids) == 0 { - return nil - } - - return DB().Where("id in ?", ids).Delete(new(MetricDescription)).Error -} diff --git a/src/models/recording_rule.go b/src/models/recording_rule.go deleted file mode 100644 index 8efddcf916d480a15cff424099df37a84e5796f8..0000000000000000000000000000000000000000 --- a/src/models/recording_rule.go +++ /dev/null @@ -1,247 +0,0 @@ -package models - -import ( - "fmt" - "strings" - "time" - - "github.com/pkg/errors" - "github.com/prometheus/common/model" -) - -// A RecordingRule records its vector expression into new timeseries. -type RecordingRule struct { - Id int64 `json:"id" gorm:"primaryKey"` - GroupId int64 `json:"group_id"` // busi group id - Cluster string `json:"cluster"` // take effect by cluster, seperated by space - Name string `json:"name"` // new metric name - Note string `json:"note"` // note - Disabled int `json:"disabled"` // 0: enabled, 1: disabled - PromQl string `json:"prom_ql"` // just one ql for promql - PromEvalInterval int `json:"prom_eval_interval"` // unit:s - AppendTags string `json:"-"` // split by space: service=n9e mod=api - AppendTagsJSON []string `json:"append_tags" gorm:"-"` // for fe - CreateAt int64 `json:"create_at"` - CreateBy string `json:"create_by"` - UpdateAt int64 `json:"update_at"` - UpdateBy string `json:"update_by"` -} - -func (re *RecordingRule) TableName() string { - return "recording_rule" -} - -func (re *RecordingRule) FE2DB() { - //re.Cluster = strings.Join(re.ClusterJSON, " ") - re.AppendTags = strings.Join(re.AppendTagsJSON, " ") -} - -func (re *RecordingRule) DB2FE() { - //re.ClusterJSON = strings.Fields(re.Cluster) - re.AppendTagsJSON = strings.Fields(re.AppendTags) -} - -func (re *RecordingRule) Verify() error { - if re.GroupId < 0 { - return fmt.Errorf("GroupId(%d) invalid", re.GroupId) - } - - if re.Cluster == "" { - return errors.New("cluster is blank") - } - - if IsClusterAll(re.Cluster) { - re.Cluster = ClusterAll - } - - if !model.MetricNameRE.MatchString(re.Name) { - return errors.New("Name has invalid chreacters") - } - - if re.Name == "" { - return errors.New("name is blank") - } - - if re.PromEvalInterval <= 0 { - re.PromEvalInterval = 60 - } - - re.AppendTags = strings.TrimSpace(re.AppendTags) - rer := strings.Fields(re.AppendTags) - for i := 0; i < len(rer); i++ { - pair := strings.Split(rer[i], "=") - if len(pair) != 2 || !model.LabelNameRE.MatchString(pair[0]) { - return fmt.Errorf("AppendTags(%s) invalid", rer[i]) - } - } - - return nil -} - -func (re *RecordingRule) Add() error { - if err := re.Verify(); err != nil { - return err - } - - // 由于实际场景中会出现name重复的recording rule,所以不需要检查重复 - //exists, err := RecordingRuleExists(0, re.GroupId, re.Cluster, re.Name) - //if err != nil { - // return err - //} - // - //if exists { - // return errors.New("RecordingRule already exists") - //} - - now := time.Now().Unix() - re.CreateAt = now - re.UpdateAt = now - - return Insert(re) -} - -func (re *RecordingRule) Update(ref RecordingRule) error { - // 由于实际场景中会出现name重复的recording rule,所以不需要检查重复 - //if re.Name != ref.Name { - // exists, err := RecordingRuleExists(re.Id, re.GroupId, re.Cluster, ref.Name) - // if err != nil { - // return err - // } - // if exists { - // return errors.New("RecordingRule already exists") - // } - //} - - ref.FE2DB() - ref.Id = re.Id - ref.GroupId = re.GroupId - ref.CreateAt = re.CreateAt - ref.CreateBy = re.CreateBy - ref.UpdateAt = time.Now().Unix() - err := ref.Verify() - if err != nil { - return err - } - return DB().Model(re).Select("*").Updates(ref).Error -} - -func (re *RecordingRule) UpdateFieldsMap(fields map[string]interface{}) error { - return DB().Model(re).Updates(fields).Error -} - -func RecordingRuleDels(ids []int64, groupId int64) error { - for i := 0; i < len(ids); i++ { - ret := DB().Where("id = ? and group_id=?", ids[i], groupId).Delete(&RecordingRule{}) - if ret.Error != nil { - return ret.Error - } - } - - return nil -} - -func RecordingRuleExists(id, groupId int64, cluster, name string) (bool, error) { - session := DB().Where("id <> ? and group_id = ? and name =? ", id, groupId, name) - - var lst []RecordingRule - err := session.Find(&lst).Error - if err != nil { - return false, err - } - if len(lst) == 0 { - return false, nil - } - - // match cluster - for _, r := range lst { - if MatchCluster(r.Cluster, cluster) { - return true, nil - } - } - return false, nil -} - -func RecordingRuleGets(groupId int64) ([]RecordingRule, error) { - session := DB().Where("group_id=?", groupId).Order("name") - - var lst []RecordingRule - err := session.Find(&lst).Error - if err == nil { - for i := 0; i < len(lst); i++ { - lst[i].DB2FE() - } - } - - return lst, err -} - -func RecordingRuleGet(where string, regs ...interface{}) (*RecordingRule, error) { - var lst []*RecordingRule - err := DB().Where(where, regs...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - lst[0].DB2FE() - - return lst[0], nil -} - -func RecordingRuleGetById(id int64) (*RecordingRule, error) { - return RecordingRuleGet("id=?", id) -} - -func RecordingRuleGetsByCluster(cluster string) ([]*RecordingRule, error) { - session := DB().Where("disabled = ?", 0) - - if cluster != "" { - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var lst []*RecordingRule - err := session.Find(&lst).Error - if err != nil { - return lst, err - } - - if len(lst) == 0 { - return lst, nil - } - - if cluster == "" { - for i := 0; i < len(lst); i++ { - lst[i].DB2FE() - } - return lst, nil - } - - lr := make([]*RecordingRule, 0, len(lst)) - for _, r := range lst { - if MatchCluster(r.Cluster, cluster) { - r.DB2FE() - lr = append(lr, r) - } - } - - return lr, err -} - -func RecordingRuleStatistics(cluster string) (*Statistics, error) { - session := DB().Model(&RecordingRule{}).Select("count(*) as total", "max(update_at) as last_updated") - if cluster != "" { - // 简略的判断,当一个clustername是另一个clustername的substring的时候,会出现stats与预期不符,不影响使用 - session = session.Where("(cluster like ? or cluster = ?)", "%"+cluster+"%", ClusterAll) - } - - var stats []*Statistics - err := session.Find(&stats).Error - if err != nil { - return nil, err - } - - return stats[0], nil -} diff --git a/src/models/role.go b/src/models/role.go deleted file mode 100644 index 00579363d14ee4b5a298324499a79aaeb104bd87..0000000000000000000000000000000000000000 --- a/src/models/role.go +++ /dev/null @@ -1,28 +0,0 @@ -package models - -import ( - "github.com/pkg/errors" -) - -type Role struct { - Id int64 `json:"id" gorm:"primaryKey"` - Name string `json:"name"` - Note string `json:"note"` -} - -func (Role) TableName() string { - return "role" -} - -func RoleGets(where string, args ...interface{}) ([]Role, error) { - var objs []Role - err := DB().Where(where, args...).Order("name").Find(&objs).Error - if err != nil { - return nil, errors.WithMessage(err, "failed to query roles") - } - return objs, nil -} - -func RoleGetsAll() ([]Role, error) { - return RoleGets("") -} diff --git a/src/models/role_operation.go b/src/models/role_operation.go deleted file mode 100644 index 68b5d925a71c3edf32844b1fd4c03d48a5a216dd..0000000000000000000000000000000000000000 --- a/src/models/role_operation.go +++ /dev/null @@ -1,34 +0,0 @@ -package models - -import ( - "github.com/toolkits/pkg/slice" -) - -type RoleOperation struct { - RoleName string - Operation string -} - -func (RoleOperation) TableName() string { - return "role_operation" -} - -func RoleHasOperation(roles []string, operation string) (bool, error) { - if len(roles) == 0 { - return false, nil - } - - return Exists(DB().Model(&RoleOperation{}).Where("operation = ? and role_name in ?", operation, roles)) -} - -func OperationsOfRole(roles []string) ([]string, error) { - session := DB().Model(&RoleOperation{}).Select("distinct(operation) as operation") - - if !slice.ContainsString(roles, AdminRole) { - session = session.Where("role_name in ?", roles) - } - - var ret []string - err := session.Pluck("operation", &ret).Error - return ret, err -} diff --git a/src/models/target.go b/src/models/target.go deleted file mode 100644 index 0db00a5f15957fe96c85060f225b84a78c3dde92..0000000000000000000000000000000000000000 --- a/src/models/target.go +++ /dev/null @@ -1,285 +0,0 @@ -package models - -import ( - "sort" - "strings" - "time" - - "github.com/pkg/errors" - "gorm.io/gorm" -) - -type Target struct { - Id int64 `json:"id" gorm:"primaryKey"` - GroupId int64 `json:"group_id"` - GroupObj *BusiGroup `json:"group_obj" gorm:"-"` - Cluster string `json:"cluster"` - Ident string `json:"ident"` - Note string `json:"note"` - Tags string `json:"-"` - TagsJSON []string `json:"tags" gorm:"-"` - TagsMap map[string]string `json:"-" gorm:"-"` // internal use, append tags to series - UpdateAt int64 `json:"update_at"` - - TargetUp float64 `json:"target_up" gorm:"-"` - LoadPerCore float64 `json:"load_per_core" gorm:"-"` - MemUtil float64 `json:"mem_util" gorm:"-"` - DiskUtil float64 `json:"disk_util" gorm:"-"` -} - -func (t *Target) TableName() string { - return "target" -} - -func (t *Target) Add() error { - obj, err := TargetGet("ident = ?", t.Ident) - if err != nil { - return err - } - - if obj == nil { - return Insert(t) - } - - if obj.Cluster != t.Cluster { - return DB().Model(&Target{}).Where("ident = ?", t.Ident).Updates(map[string]interface{}{ - "cluster": t.Cluster, - "update_at": t.UpdateAt, - }).Error - } - - return nil -} - -func (t *Target) FillGroup(cache map[int64]*BusiGroup) error { - if t.GroupId <= 0 { - return nil - } - - bg, has := cache[t.GroupId] - if has { - t.GroupObj = bg - return nil - } - - bg, err := BusiGroupGetById(t.GroupId) - if err != nil { - return errors.WithMessage(err, "failed to get busi group") - } - - t.GroupObj = bg - cache[t.GroupId] = bg - return nil -} - -func TargetStatistics(cluster string) (*Statistics, error) { - session := DB().Model(&Target{}).Select("count(*) as total", "max(update_at) as last_updated") - if cluster != "" { - session = session.Where("cluster = ?", cluster) - } - - var stats []*Statistics - err := session.Find(&stats).Error - if err != nil { - return nil, err - } - - return stats[0], nil -} - -func TargetDel(idents []string) error { - if len(idents) == 0 { - panic("idents empty") - } - return DB().Where("ident in ?", idents).Delete(new(Target)).Error -} - -func buildTargetWhere(bgid int64, clusters []string, query string) *gorm.DB { - session := DB().Model(&Target{}) - - if bgid >= 0 { - session = session.Where("group_id=?", bgid) - } - - if len(clusters) > 0 { - session = session.Where("cluster in ?", clusters) - } - - if query != "" { - arr := strings.Fields(query) - for i := 0; i < len(arr); i++ { - q := "%" + arr[i] + "%" - session = session.Where("ident like ? or note like ? or tags like ?", q, q, q) - } - } - - return session -} - -func TargetTotalCount() (int64, error) { - return Count(DB().Model(new(Target))) -} - -func TargetTotal(bgid int64, clusters []string, query string) (int64, error) { - return Count(buildTargetWhere(bgid, clusters, query)) -} - -func TargetGets(bgid int64, clusters []string, query string, limit, offset int) ([]*Target, error) { - var lst []*Target - err := buildTargetWhere(bgid, clusters, query).Order("ident").Limit(limit).Offset(offset).Find(&lst).Error - if err == nil { - for i := 0; i < len(lst); i++ { - lst[i].TagsJSON = strings.Fields(lst[i].Tags) - } - } - return lst, err -} - -func TargetGetsByCluster(cluster string) ([]*Target, error) { - session := DB().Model(&Target{}) - if cluster != "" { - session = session.Where("cluster = ?", cluster) - } - - var lst []*Target - err := session.Find(&lst).Error - return lst, err -} - -func TargetUpdateNote(idents []string, note string) error { - return DB().Model(&Target{}).Where("ident in ?", idents).Updates(map[string]interface{}{ - "note": note, - "update_at": time.Now().Unix(), - }).Error -} - -func TargetUpdateBgid(idents []string, bgid int64, clearTags bool) error { - fields := map[string]interface{}{ - "group_id": bgid, - "update_at": time.Now().Unix(), - } - - if clearTags { - fields["tags"] = "" - } - - return DB().Model(&Target{}).Where("ident in ?", idents).Updates(fields).Error -} - -func TargetGet(where string, args ...interface{}) (*Target, error) { - var lst []*Target - err := DB().Where(where, args...).Find(&lst).Error - if err != nil { - return nil, err - } - - if len(lst) == 0 { - return nil, nil - } - - lst[0].TagsJSON = strings.Fields(lst[0].Tags) - - return lst[0], nil -} - -func TargetGetById(id int64) (*Target, error) { - return TargetGet("id = ?", id) -} - -func TargetGetByIdent(ident string) (*Target, error) { - return TargetGet("ident = ?", ident) -} - -func TargetGetTags(idents []string) ([]string, error) { - if len(idents) == 0 { - return []string{}, nil - } - - var arr []string - err := DB().Model(new(Target)).Where("ident in ?", idents).Select("distinct(tags) as tags").Pluck("tags", &arr).Error - if err != nil { - return nil, err - } - - cnt := len(arr) - if cnt == 0 { - return []string{}, nil - } - - set := make(map[string]struct{}) - for i := 0; i < cnt; i++ { - tags := strings.Fields(arr[i]) - for j := 0; j < len(tags); j++ { - set[tags[j]] = struct{}{} - } - } - - cnt = len(set) - ret := make([]string, 0, cnt) - for key := range set { - ret = append(ret, key) - } - - sort.Strings(ret) - - return ret, err -} - -func (t *Target) AddTags(tags []string) error { - for i := 0; i < len(tags); i++ { - if -1 == strings.Index(t.Tags, tags[i]+" ") { - t.Tags += tags[i] + " " - } - } - - arr := strings.Fields(t.Tags) - sort.Strings(arr) - - return DB().Model(t).Updates(map[string]interface{}{ - "tags": strings.Join(arr, " ") + " ", - "update_at": time.Now().Unix(), - }).Error -} - -func (t *Target) DelTags(tags []string) error { - for i := 0; i < len(tags); i++ { - t.Tags = strings.ReplaceAll(t.Tags, tags[i]+" ", "") - } - - return DB().Model(t).Updates(map[string]interface{}{ - "tags": t.Tags, - "update_at": time.Now().Unix(), - }).Error -} - -func TargetIdents(ids []int64) ([]string, error) { - var ret []string - - if len(ids) == 0 { - return ret, nil - } - - err := DB().Model(&Target{}).Where("id in ?", ids).Pluck("ident", &ret).Error - return ret, err -} - -func TargetIds(idents []string) ([]int64, error) { - var ret []int64 - - if len(idents) == 0 { - return ret, nil - } - - err := DB().Model(&Target{}).Where("ident in ?", idents).Pluck("id", &ret).Error - return ret, err -} - -func IdentsFilter(idents []string, where string, args ...interface{}) ([]string, error) { - var arr []string - if len(idents) == 0 { - return arr, nil - } - - err := DB().Model(&Target{}).Where("ident in ?", idents).Where(where, args...).Pluck("ident", &arr).Error - return arr, err -} diff --git a/src/models/user_group_member.go b/src/models/user_group_member.go deleted file mode 100644 index 8d5365683c6dd258ecf408f5fda90596d1265b15..0000000000000000000000000000000000000000 --- a/src/models/user_group_member.go +++ /dev/null @@ -1,59 +0,0 @@ -package models - -type UserGroupMember struct { - GroupId int64 - UserId int64 -} - -func (UserGroupMember) TableName() string { - return "user_group_member" -} - -func MyGroupIds(userId int64) ([]int64, error) { - var ids []int64 - err := DB().Model(&UserGroupMember{}).Where("user_id=?", userId).Pluck("group_id", &ids).Error - return ids, err -} - -func MemberIds(groupId int64) ([]int64, error) { - var ids []int64 - err := DB().Model(&UserGroupMember{}).Where("group_id=?", groupId).Pluck("user_id", &ids).Error - return ids, err -} - -func UserGroupMemberCount(where string, args ...interface{}) (int64, error) { - return Count(DB().Model(&UserGroupMember{}).Where(where, args...)) -} - -func UserGroupMemberAdd(groupId, userId int64) error { - num, err := UserGroupMemberCount("user_id=? and group_id=?", userId, groupId) - if err != nil { - return err - } - - if num > 0 { - // already exists - return nil - } - - obj := UserGroupMember{ - GroupId: groupId, - UserId: userId, - } - - return Insert(obj) -} - -func UserGroupMemberDel(groupId int64, userIds []int64) error { - if len(userIds) == 0 { - return nil - } - - return DB().Where("group_id = ? and user_id in ?", groupId, userIds).Delete(&UserGroupMember{}).Error -} - -func UserGroupMemberGetAll() ([]UserGroupMember, error) { - var lst []UserGroupMember - err := DB().Find(&lst).Error - return lst, err -} diff --git a/src/notifier/notifier.go b/src/notifier/notifier.go deleted file mode 100644 index 3fdda89efa03c79cc62314536e59c33fbf51f1b4..0000000000000000000000000000000000000000 --- a/src/notifier/notifier.go +++ /dev/null @@ -1,9 +0,0 @@ -package notifier - -type Notifier interface { - Descript() string - Notify([]byte) - NotifyMaintainer([]byte) -} - -var Instance Notifier diff --git a/src/pkg/sys/cmd_unix.go b/src/pkg/sys/cmd_unix.go deleted file mode 100644 index 01418be7236fb19d377652387b896da21f2a8a9f..0000000000000000000000000000000000000000 --- a/src/pkg/sys/cmd_unix.go +++ /dev/null @@ -1,32 +0,0 @@ -//go:build !windows -// +build !windows - -package sys - -import ( - "os/exec" - "syscall" - "time" -) - -func WrapTimeout(cmd *exec.Cmd, timeout time.Duration) (error, bool) { - var err error - - done := make(chan error) - go func() { - done <- cmd.Wait() - }() - - select { - case <-time.After(timeout): - go func() { - <-done // allow goroutine to exit - }() - - // IMPORTANT: cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} is necessary before cmd.Start() - err = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) - return err, true - case err = <-done: - return err, false - } -} diff --git a/src/pkg/sys/cmd_windows.go b/src/pkg/sys/cmd_windows.go deleted file mode 100644 index 0ef38ba82be7519c27625d6783fdb4b6b0d589f0..0000000000000000000000000000000000000000 --- a/src/pkg/sys/cmd_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -package sys - -import ( - "os/exec" - "syscall" - "time" -) - -func WrapTimeout(cmd *exec.Cmd, timeout time.Duration) (error, bool) { - var err error - - done := make(chan error) - go func() { - done <- cmd.Wait() - }() - - select { - case <-time.After(timeout): - go func() { - <-done // allow goroutine to exit - }() - - err = cmd.Process.Signal(syscall.SIGKILL) - return err, true - case err = <-done: - return err, false - } -} diff --git a/src/pkg/tls/utils.go b/src/pkg/tls/utils.go deleted file mode 100644 index 65388640f7dd864b61ce9214187ff781285a7dd4..0000000000000000000000000000000000000000 --- a/src/pkg/tls/utils.go +++ /dev/null @@ -1,30 +0,0 @@ -package tls - -import ( - "fmt" -) - -// ParseCiphers returns a `[]uint16` by received `[]string` key that represents ciphers from crypto/tls. -// If some of ciphers in received list doesn't exists ParseCiphers returns nil with error -func ParseCiphers(ciphers []string) ([]uint16, error) { - suites := []uint16{} - - for _, cipher := range ciphers { - v, ok := tlsCipherMap[cipher] - if !ok { - return nil, fmt.Errorf("unsupported cipher %q", cipher) - } - suites = append(suites, v) - } - - return suites, nil -} - -// ParseTLSVersion returns a `uint16` by received version string key that represents tls version from crypto/tls. -// If version isn't supported ParseTLSVersion returns 0 with error -func ParseTLSVersion(version string) (uint16, error) { - if v, ok := tlsVersionMap[version]; ok { - return v, nil - } - return 0, fmt.Errorf("unsupported version %q", version) -} diff --git a/src/pkg/version/version.go b/src/pkg/version/version.go deleted file mode 100644 index d0dc2a1f31d24cc7c54791e69d0858d881081fb2..0000000000000000000000000000000000000000 --- a/src/pkg/version/version.go +++ /dev/null @@ -1,4 +0,0 @@ -package version - -// VERSION go build -ldflags "-X pkg.version.VERSION=x.x.x" -var VERSION = "not specified" diff --git a/src/server/common/label_append.go b/src/server/common/label_append.go deleted file mode 100644 index 9d2f19239a0e0fab7c45e583cd2d32a90cfa7c7d..0000000000000000000000000000000000000000 --- a/src/server/common/label_append.go +++ /dev/null @@ -1,55 +0,0 @@ -package common - -import ( - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/prometheus/prometheus/prompb" -) - -func AppendLabels(pt *prompb.TimeSeries, target *models.Target) { - if target == nil { - return - } - - labelKeys := make(map[string]int) - for j := 0; j < len(pt.Labels); j++ { - labelKeys[pt.Labels[j].Name] = j - } - - for key, value := range target.TagsMap { - if index, has := labelKeys[key]; has { - // overwrite labels - if config.C.LabelRewrite { - pt.Labels[index].Value = value - } - continue - } - - pt.Labels = append(pt.Labels, &prompb.Label{ - Name: key, - Value: value, - }) - } - - // e.g. busigroup=cloud - if _, has := labelKeys[config.C.BusiGroupLabelKey]; has { - return - } - // 将业务组名称作为tag附加到数据上 - if target.GroupId > 0 && len(config.C.BusiGroupLabelKey) > 0 { - bg := memsto.BusiGroupCache.GetByBusiGroupId(target.GroupId) - if bg == nil { - return - } - - if bg.LabelEnable == 0 { - return - } - - pt.Labels = append(pt.Labels, &prompb.Label{ - Name: config.C.BusiGroupLabelKey, - Value: bg.LabelValue, - }) - } -} diff --git a/src/server/common/sender/plugin.go b/src/server/common/sender/plugin.go deleted file mode 100644 index c28f7de2c0a2f0075a33da75edd5e8a016d7f691..0000000000000000000000000000000000000000 --- a/src/server/common/sender/plugin.go +++ /dev/null @@ -1,77 +0,0 @@ -package sender - -import ( - "bytes" - "os/exec" - "time" - - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/notifier" - "github.com/didi/nightingale/v5/src/pkg/sys" - "github.com/didi/nightingale/v5/src/server/config" -) - -func MayPluginNotify(noticeBytes []byte) { - if len(noticeBytes) == 0 { - return - } - alertingCallPlugin(noticeBytes) - alertingCallScript(noticeBytes) -} - -func alertingCallScript(stdinBytes []byte) { - // not enable or no notify.py? do nothing - if !config.C.Alerting.CallScript.Enable || config.C.Alerting.CallScript.ScriptPath == "" { - return - } - - fpath := config.C.Alerting.CallScript.ScriptPath - cmd := exec.Command(fpath) - cmd.Stdin = bytes.NewReader(stdinBytes) - - // combine stdout and stderr - var buf bytes.Buffer - cmd.Stdout = &buf - cmd.Stderr = &buf - - err := startCmd(cmd) - if err != nil { - logger.Errorf("event_notify: run cmd err: %v", err) - return - } - - err, isTimeout := sys.WrapTimeout(cmd, time.Duration(config.C.Alerting.Timeout)*time.Millisecond) - - if isTimeout { - if err == nil { - logger.Errorf("event_notify: timeout and killed process %s", fpath) - } - - if err != nil { - logger.Errorf("event_notify: kill process %s occur error %v", fpath, err) - } - - return - } - - if err != nil { - logger.Errorf("event_notify: exec script %s occur error: %v, output: %s", fpath, err, buf.String()) - return - } - - logger.Infof("event_notify: exec %s output: %s", fpath, buf.String()) -} - -// call notify.so via golang plugin build -// ig. etc/script/notify/notify.so -func alertingCallPlugin(stdinBytes []byte) { - if !config.C.Alerting.CallPlugin.Enable { - return - } - - logger.Debugf("alertingCallPlugin begin") - logger.Debugf("payload:", string(stdinBytes)) - notifier.Instance.Notify(stdinBytes) - logger.Debugf("alertingCallPlugin done") -} diff --git a/src/server/common/sender/redis_pub.go b/src/server/common/sender/redis_pub.go deleted file mode 100644 index 8e8ab1f801f12fa7e2728b2ee085e5c8cad7c5c5..0000000000000000000000000000000000000000 --- a/src/server/common/sender/redis_pub.go +++ /dev/null @@ -1,26 +0,0 @@ -package sender - -import ( - "context" - - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/storage" -) - -func PublishToRedis(clusterName string, bs []byte) { - if len(bs) == 0 { - return - } - if !config.C.Alerting.RedisPub.Enable { - return - } - - // pub all alerts to redis - channelKey := config.C.Alerting.RedisPub.ChannelPrefix + clusterName - err := storage.Redis.Publish(context.Background(), channelKey, bs).Err() - if err != nil { - logger.Errorf("event_notify: redis publish %s err: %v", channelKey, err) - } -} diff --git a/src/server/config/config.go b/src/server/config/config.go deleted file mode 100644 index 3539669cad42bbdfb85fe9766e19d0c526445208..0000000000000000000000000000000000000000 --- a/src/server/config/config.go +++ /dev/null @@ -1,450 +0,0 @@ -package config - -import ( - "fmt" - "html/template" - "log" - "net" - "os" - "path" - "plugin" - "runtime" - "strings" - "sync" - "time" - - "github.com/gin-gonic/gin" - "github.com/koding/multiconfig" - "github.com/pkg/errors" - "github.com/toolkits/pkg/file" - "github.com/toolkits/pkg/runner" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/notifier" - "github.com/didi/nightingale/v5/src/pkg/httpx" - "github.com/didi/nightingale/v5/src/pkg/logx" - "github.com/didi/nightingale/v5/src/pkg/ormx" - "github.com/didi/nightingale/v5/src/pkg/secu" - "github.com/didi/nightingale/v5/src/pkg/tplx" - "github.com/didi/nightingale/v5/src/storage" -) - -var ( - C = new(Config) - once sync.Once -) - -func DealConfigCrypto(key string) { - decryptDsn, err := secu.DealWithDecrypt(C.DB.DSN, key) - if err != nil { - fmt.Println("failed to decrypt the db dsn", err) - os.Exit(1) - } - C.DB.DSN = decryptDsn - - decryptRedisPwd, err := secu.DealWithDecrypt(C.Redis.Password, key) - if err != nil { - fmt.Println("failed to decrypt the redis password", err) - os.Exit(1) - } - C.Redis.Password = decryptRedisPwd - - decryptSmtpPwd, err := secu.DealWithDecrypt(C.SMTP.Pass, key) - if err != nil { - fmt.Println("failed to decrypt the smtp password", err) - os.Exit(1) - } - C.SMTP.Pass = decryptSmtpPwd - - decryptHookPwd, err := secu.DealWithDecrypt(C.Alerting.Webhook.BasicAuthPass, key) - if err != nil { - fmt.Println("failed to decrypt the alert webhook password", err) - os.Exit(1) - } - C.Alerting.Webhook.BasicAuthPass = decryptHookPwd - - decryptIbexPwd, err := secu.DealWithDecrypt(C.Ibex.BasicAuthPass, key) - if err != nil { - fmt.Println("failed to decrypt the ibex password", err) - os.Exit(1) - } - C.Ibex.BasicAuthPass = decryptIbexPwd - - if len(C.Readers) == 0 { - C.Reader.ClusterName = C.ClusterName - C.Readers = append(C.Readers, C.Reader) - } - - for index, v := range C.Readers { - decryptReaderPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, key) - if err != nil { - fmt.Printf("failed to decrypt the reader password: %s , error: %s", v.BasicAuthPass, err.Error()) - os.Exit(1) - } - C.Readers[index].BasicAuthPass = decryptReaderPwd - } - - for index, v := range C.Writers { - decryptWriterPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, key) - if err != nil { - fmt.Printf("failed to decrypt the writer password: %s , error: %s", v.BasicAuthPass, err.Error()) - os.Exit(1) - } - C.Writers[index].BasicAuthPass = decryptWriterPwd - } - -} - -func MustLoad(key string, fpaths ...string) { - once.Do(func() { - loaders := []multiconfig.Loader{ - &multiconfig.TagLoader{}, - &multiconfig.EnvironmentLoader{}, - } - - for _, fpath := range fpaths { - handled := false - - if strings.HasSuffix(fpath, "toml") { - loaders = append(loaders, &multiconfig.TOMLLoader{Path: fpath}) - handled = true - } - if strings.HasSuffix(fpath, "conf") { - loaders = append(loaders, &multiconfig.TOMLLoader{Path: fpath}) - handled = true - } - if strings.HasSuffix(fpath, "json") { - loaders = append(loaders, &multiconfig.JSONLoader{Path: fpath}) - handled = true - } - if strings.HasSuffix(fpath, "yaml") { - loaders = append(loaders, &multiconfig.YAMLLoader{Path: fpath}) - handled = true - } - - if !handled { - fmt.Println("config file invalid, valid file exts: .conf,.yaml,.toml,.json") - os.Exit(1) - } - } - - m := multiconfig.DefaultLoader{ - Loader: multiconfig.MultiLoader(loaders...), - Validator: multiconfig.MultiValidator(&multiconfig.RequiredValidator{}), - } - m.MustLoad(C) - - DealConfigCrypto(key) - - if C.EngineDelay == 0 { - C.EngineDelay = 120 - } - - if C.ReaderFrom == "" { - C.ReaderFrom = "config" - } - - if C.ReaderFrom == "config" && C.ClusterName == "" { - fmt.Println("configuration ClusterName is blank") - os.Exit(1) - } - - if C.Heartbeat.IP == "" { - // auto detect - // C.Heartbeat.IP = fmt.Sprint(GetOutboundIP()) - // 自动获取IP在有些环境下容易出错,这里用hostname+pid来作唯一标识 - - hostname, err := os.Hostname() - if err != nil { - fmt.Println("failed to get hostname:", err) - os.Exit(1) - } - - if strings.Contains(hostname, "localhost") { - fmt.Println("Warning! hostname contains substring localhost, setting a more unique hostname is recommended") - } - - C.Heartbeat.IP = hostname - - // if C.Heartbeat.IP == "" { - // fmt.Println("heartbeat ip auto got is blank") - // os.Exit(1) - // } - } - - C.Heartbeat.Endpoint = fmt.Sprintf("%s:%d", C.Heartbeat.IP, C.HTTP.Port) - - C.Alerting.check() - - if C.WriterOpt.QueueMaxSize <= 0 { - C.WriterOpt.QueueMaxSize = 10000000 - } - - if C.WriterOpt.QueuePopSize <= 0 { - C.WriterOpt.QueuePopSize = 1000 - } - - if C.WriterOpt.QueueCount <= 0 { - C.WriterOpt.QueueCount = 1000 - } - - if C.WriterOpt.ShardingKey == "" { - C.WriterOpt.ShardingKey = "ident" - } - - for i, write := range C.Writers { - if C.Writers[i].ClusterName == "" { - C.Writers[i].ClusterName = C.ClusterName - } - - for _, relabel := range write.WriteRelabels { - regex, ok := relabel.Regex.(string) - if !ok { - log.Println("Regex field must be a string") - os.Exit(1) - } - - if regex == "" { - regex = "(.*)" - } - relabel.Regex = models.MustNewRegexp(regex) - - if relabel.Separator == "" { - relabel.Separator = ";" - } - - if relabel.Action == "" { - relabel.Action = "replace" - } - - if relabel.Replacement == "" { - relabel.Replacement = "$1" - } - } - } - - fmt.Println("heartbeat.ip:", C.Heartbeat.IP) - fmt.Printf("heartbeat.interval: %dms\n", C.Heartbeat.Interval) - }) -} - -type Config struct { - RunMode string - ClusterName string // 监控对象上报时,指定的集群名称 - BusiGroupLabelKey string - EngineDelay int64 - DisableUsageReport bool - ReaderFrom string - LabelRewrite bool - ForceUseServerTS bool - Log logx.Config - HTTP httpx.Config - BasicAuth gin.Accounts - SMTP SMTPConfig - Heartbeat HeartbeatConfig - Alerting Alerting - NoData NoData - Redis storage.RedisConfig - DB ormx.DBConfig - WriterOpt WriterGlobalOpt - Writers []WriterOptions - Reader PromOption - Readers []PromOption - Ibex Ibex -} - -type WriterOptions struct { - ClusterName string - Url string - BasicAuthUser string - BasicAuthPass string - - Timeout int64 - DialTimeout int64 - TLSHandshakeTimeout int64 - ExpectContinueTimeout int64 - IdleConnTimeout int64 - KeepAlive int64 - - MaxConnsPerHost int - MaxIdleConns int - MaxIdleConnsPerHost int - - Headers []string - - WriteRelabels []*models.RelabelConfig -} - -type WriterGlobalOpt struct { - QueueCount int - QueueMaxSize int - QueuePopSize int - ShardingKey string -} - -type HeartbeatConfig struct { - IP string - Interval int64 - Endpoint string -} - -type SMTPConfig struct { - Host string - Port int - User string - Pass string - From string - InsecureSkipVerify bool - Batch int -} - -type Alerting struct { - Timeout int64 - TemplatesDir string - NotifyConcurrency int - NotifyBuiltinChannels []string - CallScript CallScript - CallPlugin CallPlugin - RedisPub RedisPub - Webhook Webhook -} - -func (a *Alerting) check() { - if a.Webhook.Enable { - if a.Webhook.Timeout == "" { - a.Webhook.TimeoutDuration = time.Second * 5 - } else { - dur, err := time.ParseDuration(C.Alerting.Webhook.Timeout) - if err != nil { - fmt.Println("failed to parse Alerting.Webhook.Timeout") - os.Exit(1) - } - a.Webhook.TimeoutDuration = dur - } - } - - if a.CallPlugin.Enable { - if runtime.GOOS == "windows" { - fmt.Println("notify plugin on unsupported os:", runtime.GOOS) - os.Exit(1) - } - - p, err := plugin.Open(a.CallPlugin.PluginPath) - if err != nil { - fmt.Println("failed to load plugin:", err) - os.Exit(1) - } - - caller, err := p.Lookup(a.CallPlugin.Caller) - if err != nil { - fmt.Println("failed to lookup plugin Caller:", err) - os.Exit(1) - } - - ins, ok := caller.(notifier.Notifier) - if !ok { - log.Println("notifier interface not implemented") - os.Exit(1) - } - - notifier.Instance = ins - } - - if a.TemplatesDir == "" { - a.TemplatesDir = path.Join(runner.Cwd, "etc", "template") - } - - if a.Timeout == 0 { - a.Timeout = 30000 - } -} - -func (a *Alerting) ListTpls() (map[string]*template.Template, error) { - filenames, err := file.FilesUnder(a.TemplatesDir) - if err != nil { - return nil, errors.WithMessage(err, "failed to exec FilesUnder") - } - - if len(filenames) == 0 { - return nil, errors.New("no tpl files under " + a.TemplatesDir) - } - - tplFiles := make([]string, 0, len(filenames)) - for i := 0; i < len(filenames); i++ { - if strings.HasSuffix(filenames[i], ".tpl") { - tplFiles = append(tplFiles, filenames[i]) - } - } - - if len(tplFiles) == 0 { - return nil, errors.New("no tpl files under " + a.TemplatesDir) - } - - tpls := make(map[string]*template.Template) - for _, tplFile := range tplFiles { - tplpath := path.Join(a.TemplatesDir, tplFile) - tpl, err := template.New(tplFile).Funcs(tplx.TemplateFuncMap).ParseFiles(tplpath) - if err != nil { - return nil, errors.WithMessage(err, "failed to parse tpl: "+tplpath) - } - tpls[tplFile] = tpl - } - return tpls, nil -} - -type CallScript struct { - Enable bool - ScriptPath string -} - -type CallPlugin struct { - Enable bool - PluginPath string - Caller string -} - -type RedisPub struct { - Enable bool - ChannelPrefix string - ChannelKey string -} - -type Webhook struct { - Enable bool - Url string - BasicAuthUser string - BasicAuthPass string - Timeout string - TimeoutDuration time.Duration - Headers []string -} - -type NoData struct { - Metric string - Interval int64 -} - -type Ibex struct { - Address string - BasicAuthUser string - BasicAuthPass string - Timeout int64 -} - -func (c *Config) IsDebugMode() bool { - return c.RunMode == "debug" -} - -// Get preferred outbound ip of this machine -func GetOutboundIP() net.IP { - conn, err := net.Dial("udp", "223.5.5.5:80") - if err != nil { - fmt.Println("auto get outbound ip fail:", err) - os.Exit(1) - } - defer conn.Close() - - localAddr := conn.LocalAddr().(*net.UDPAddr) - - return localAddr.IP -} diff --git a/src/server/config/prom_client.go b/src/server/config/prom_client.go deleted file mode 100644 index 65010cdbd9ee07e2cbb420811e6457bc2529e1da..0000000000000000000000000000000000000000 --- a/src/server/config/prom_client.go +++ /dev/null @@ -1,92 +0,0 @@ -package config - -import ( - "strings" - "sync" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/prom" -) - -type PromClientMap struct { - sync.RWMutex - Clients map[string]prom.API -} - -var ReaderClients = &PromClientMap{Clients: make(map[string]prom.API)} - -func (pc *PromClientMap) Set(clusterName string, c prom.API) { - if c == nil { - return - } - pc.Lock() - defer pc.Unlock() - pc.Clients[clusterName] = c -} - -func (pc *PromClientMap) GetClusterNames() []string { - pc.RLock() - defer pc.RUnlock() - var clusterNames []string - for k := range pc.Clients { - clusterNames = append(clusterNames, k) - } - - return clusterNames -} - -func (pc *PromClientMap) GetCli(cluster string) prom.API { - pc.RLock() - defer pc.RUnlock() - c := pc.Clients[cluster] - return c -} - -func (pc *PromClientMap) IsNil(cluster string) bool { - pc.RLock() - defer pc.RUnlock() - - c, exists := pc.Clients[cluster] - if !exists { - return true - } - - return c == nil -} - -// Hit 根据当前有效的cluster和规则的cluster配置计算有效的cluster列表 -func (pc *PromClientMap) Hit(cluster string) []string { - pc.RLock() - defer pc.RUnlock() - clusters := make([]string, 0, len(pc.Clients)) - if cluster == models.ClusterAll { - for c := range pc.Clients { - clusters = append(clusters, c) - } - return clusters - } - - ruleClusters := strings.Fields(cluster) - for c := range pc.Clients { - for _, rc := range ruleClusters { - if rc == c { - clusters = append(clusters, c) - continue - } - } - } - return clusters -} - -func (pc *PromClientMap) Reset() { - pc.Lock() - defer pc.Unlock() - - pc.Clients = make(map[string]prom.API) -} - -func (pc *PromClientMap) Del(cluster string) { - pc.Lock() - defer pc.Unlock() - delete(pc.Clients, cluster) -} diff --git a/src/server/config/reader.go b/src/server/config/reader.go deleted file mode 100644 index 66fa80ba33fb8dfbeb5d82d7e7fa6fa40f2db590..0000000000000000000000000000000000000000 --- a/src/server/config/reader.go +++ /dev/null @@ -1,173 +0,0 @@ -package config - -import ( - "encoding/json" - "fmt" - "net" - "net/http" - "strings" - "time" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/prom" - "github.com/prometheus/client_golang/api" - "github.com/toolkits/pkg/logger" -) - -func InitReader() error { - rf := strings.ToLower(strings.TrimSpace(C.ReaderFrom)) - if rf == "" || rf == "config" { - if len(C.Readers) == 0 { - C.Reader.ClusterName = C.ClusterName - C.Readers = append(C.Readers, C.Reader) - } - - for _, reader := range C.Readers { - err := setClientFromPromOption(reader.ClusterName, reader) - if err != nil { - logger.Errorf("failed to setClientFromPromOption: %v", err) - continue - } - } - return nil - } - - if rf == "database" { - return initFromDatabase() - } - - return fmt.Errorf("invalid configuration ReaderFrom: %s", rf) -} - -func initFromDatabase() error { - go func() { - for { - loadFromDatabase() - time.Sleep(time.Second) - } - }() - return nil -} - -func loadFromDatabase() { - clusters, err := models.AlertingEngineGetClusters(C.Heartbeat.Endpoint) - if err != nil { - logger.Errorf("failed to get current cluster, error: %v", err) - return - } - - if len(clusters) == 0 { - ReaderClients.Reset() - logger.Warning("no datasource binded to me") - return - } - - newCluster := make(map[string]struct{}) - for _, cluster := range clusters { - newCluster[cluster] = struct{}{} - ckey := "prom." + cluster + ".option" - cval, err := models.ConfigsGet(ckey) - if err != nil { - logger.Errorf("failed to get ckey: %s, error: %v", ckey, err) - continue - } - - if cval == "" { - logger.Debugf("ckey: %s is empty", ckey) - continue - } - - var po PromOption - err = json.Unmarshal([]byte(cval), &po) - if err != nil { - logger.Errorf("failed to unmarshal PromOption: %s", err) - continue - } - - if ReaderClients.IsNil(cluster) { - // first time - if err = setClientFromPromOption(cluster, po); err != nil { - logger.Errorf("failed to setClientFromPromOption: %v", err) - continue - } - - logger.Info("setClientFromPromOption success: ", cluster) - PromOptions.Set(cluster, po) - continue - } - - localPo, has := PromOptions.Get(cluster) - if !has || !localPo.Equal(po) { - if err = setClientFromPromOption(cluster, po); err != nil { - logger.Errorf("failed to setClientFromPromOption: %v", err) - continue - } - - PromOptions.Set(cluster, po) - } - } - - // delete useless cluster - oldClusters := ReaderClients.GetClusterNames() - for _, oldCluster := range oldClusters { - if _, has := newCluster[oldCluster]; !has { - ReaderClients.Del(oldCluster) - PromOptions.Del(oldCluster) - logger.Info("delete cluster: ", oldCluster) - } - } -} - -func newClientFromPromOption(po PromOption) (api.Client, error) { - transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: time.Duration(po.DialTimeout) * time.Millisecond, - }).DialContext, - ResponseHeaderTimeout: time.Duration(po.Timeout) * time.Millisecond, - MaxIdleConnsPerHost: po.MaxIdleConnsPerHost, - } - - if po.UseTLS { - tlsConfig, err := po.TLSConfig() - if err != nil { - logger.Errorf("new cluster %s fail: %v", po.Url, err) - return nil, err - } - transport.TLSClientConfig = tlsConfig - } - - return api.NewClient(api.Config{ - Address: po.Url, - RoundTripper: transport, - }) -} - -func setClientFromPromOption(clusterName string, po PromOption) error { - if clusterName == "" { - return fmt.Errorf("argument clusterName is blank") - } - - if po.Url == "" { - return fmt.Errorf("prometheus url is blank") - } - - if strings.HasPrefix(po.Url, "https") { - po.UseTLS = true - po.InsecureSkipVerify = true - } - - cli, err := newClientFromPromOption(po) - if err != nil { - return fmt.Errorf("failed to newClientFromPromOption: %v", err) - } - - logger.Debugf("setClientFromPromOption: %s, %+v", clusterName, po) - ReaderClients.Set(clusterName, prom.NewAPI(cli, prom.ClientOptions{ - BasicAuthUser: po.BasicAuthUser, - BasicAuthPass: po.BasicAuthPass, - Headers: po.Headers, - })) - - return nil -} diff --git a/src/server/engine/engine.go b/src/server/engine/engine.go deleted file mode 100644 index 2ec367dc5142aa623776c5b633a75652aa92605f..0000000000000000000000000000000000000000 --- a/src/server/engine/engine.go +++ /dev/null @@ -1,60 +0,0 @@ -package engine - -import ( - "context" - "fmt" - "time" - - "github.com/didi/nightingale/v5/src/server/common/sender" - "github.com/didi/nightingale/v5/src/server/config" - promstat "github.com/didi/nightingale/v5/src/server/stat" - "github.com/toolkits/pkg/container/list" - "github.com/toolkits/pkg/logger" -) - -var EventQueue = list.NewSafeListLimited(10000000) - -func Start(ctx context.Context) error { - err := reloadTpls() - if err != nil { - return err - } - - // start loop consumer - go loopConsume(ctx) - - go ruleHolder.LoopSyncRules(ctx) - - go reportQueueSize() - - go sender.StartEmailSender() - - go initReporter(func(em map[ErrorType]uint64) { - if len(em) == 0 { - return - } - title := fmt.Sprintf("server %s has some errors, please check server logs for detail", config.C.Heartbeat.IP) - msg := "" - for k, v := range em { - msg += fmt.Sprintf("error: %s, count: %d\n", k, v) - } - notifyToMaintainer(title, msg) - }) - - return nil -} - -func Reload() { - err := reloadTpls() - if err != nil { - logger.Error("engine reload err:", err) - } -} - -func reportQueueSize() { - for { - time.Sleep(time.Second) - - promstat.GaugeAlertQueueSize.Set(float64(EventQueue.Len())) - } -} diff --git a/src/server/engine/notify.go b/src/server/engine/notify.go deleted file mode 100644 index 9bb0d69547ec54d2ffb0ef7d91a18c8bd5f3f09c..0000000000000000000000000000000000000000 --- a/src/server/engine/notify.go +++ /dev/null @@ -1,184 +0,0 @@ -package engine - -import ( - "bytes" - "encoding/json" - "html/template" - "sync" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/common/sender" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/toolkits/pkg/logger" -) - -var ( - rwLock sync.RWMutex - tpls map[string]*template.Template - Senders map[string]sender.Sender - - // 处理事件到subscription关系,处理的subscription用OrMerge进行合并 - routers = []Router{GroupRouter, GlobalWebhookRouter, EventCallbacksRouter} - // 额外去掉一些订阅,处理的subscription用AndMerge进行合并, 如设置 channel=false,合并后不通过这个channel发送 - // 如果实现了相关Router,可以添加到interceptors中 - interceptors []Router - - // 额外的订阅event逻辑处理 - subscribeRouters = []Router{GroupRouter} - subscribeInterceptors []Router -) - -func reloadTpls() error { - tmpTpls, err := config.C.Alerting.ListTpls() - if err != nil { - return err - } - - senders := map[string]sender.Sender{ - models.Email: sender.NewSender(models.Email, tmpTpls), - models.Dingtalk: sender.NewSender(models.Dingtalk, tmpTpls), - models.Wecom: sender.NewSender(models.Wecom, tmpTpls), - models.Feishu: sender.NewSender(models.Feishu, tmpTpls), - models.Mm: sender.NewSender(models.Mm, tmpTpls), - models.Telegram: sender.NewSender(models.Telegram, tmpTpls), - } - - rwLock.Lock() - tpls = tmpTpls - Senders = senders - rwLock.Unlock() - return nil -} - -// HandleEventNotify 处理event事件的主逻辑 -// event: 告警/恢复事件 -// isSubscribe: 告警事件是否由subscribe的配置产生 -func HandleEventNotify(event *models.AlertCurEvent, isSubscribe bool) { - rule := memsto.AlertRuleCache.Get(event.RuleId) - if rule == nil { - return - } - fillUsers(event) - - var ( - handlers []Router - interceptorHandlers []Router - ) - if isSubscribe { - handlers = subscribeRouters - interceptorHandlers = subscribeInterceptors - } else { - handlers = routers - interceptorHandlers = interceptors - } - - subscription := NewSubscription() - // 处理订阅关系使用OrMerge - for _, handler := range handlers { - subscription.OrMerge(handler(rule, event, subscription)) - } - - // 处理移除订阅关系的逻辑,比如员工离职,临时静默某个通道的策略等 - for _, handler := range interceptorHandlers { - subscription.AndMerge(handler(rule, event, subscription)) - } - - // 处理事件发送,这里用一个goroutine处理一个event的所有发送事件 - go Send(rule, event, subscription, isSubscribe) - - // 如果是不是订阅规则出现的event,则需要处理订阅规则的event - if !isSubscribe { - handleSubs(event) - } -} - -func handleSubs(event *models.AlertCurEvent) { - // handle alert subscribes - subscribes := make([]*models.AlertSubscribe, 0) - // rule specific subscribes - if subs, has := memsto.AlertSubscribeCache.Get(event.RuleId); has { - subscribes = append(subscribes, subs...) - } - // global subscribes - if subs, has := memsto.AlertSubscribeCache.Get(0); has { - subscribes = append(subscribes, subs...) - } - for _, sub := range subscribes { - handleSub(sub, *event) - } -} - -// handleSub 处理订阅规则的event,注意这里event要使用值传递,因为后面会修改event的状态 -func handleSub(sub *models.AlertSubscribe, event models.AlertCurEvent) { - if sub.IsDisabled() || !sub.MatchCluster(event.Cluster) { - return - } - if !matchTags(event.TagsMap, sub.ITags) { - return - } - - sub.ModifyEvent(&event) - LogEvent(&event, "subscribe") - HandleEventNotify(&event, true) -} - -func Send(rule *models.AlertRule, event *models.AlertCurEvent, subscription *Subscription, isSubscribe bool) { - for channel, uids := range subscription.ToChannelUserMap() { - ctx := sender.BuildMessageContext(rule, event, uids) - rwLock.RLock() - s := Senders[channel] - rwLock.RUnlock() - if s == nil { - logger.Warningf("no sender for channel: %s", channel) - continue - } - s.Send(ctx) - } - - // handle event callbacks - sender.SendCallbacks(subscription.ToCallbackList(), event) - - // handle global webhooks - sender.SendWebhooks(subscription.ToWebhookList(), event) - - noticeBytes := genNoticeBytes(event) - - // handle plugin call - go sender.MayPluginNotify(noticeBytes) - - if !isSubscribe { - // handle redis pub - sender.PublishToRedis(event.Cluster, noticeBytes) - } -} - -type Notice struct { - Event *models.AlertCurEvent `json:"event"` - Tpls map[string]string `json:"tpls"` -} - -func genNoticeBytes(event *models.AlertCurEvent) []byte { - // build notice body with templates - ntpls := make(map[string]string) - - rwLock.RLock() - defer rwLock.RUnlock() - for filename, tpl := range tpls { - var body bytes.Buffer - if err := tpl.Execute(&body, event); err != nil { - ntpls[filename] = err.Error() - } else { - ntpls[filename] = body.String() - } - } - - notice := Notice{Event: event, Tpls: ntpls} - stdinBytes, err := json.Marshal(notice) - if err != nil { - logger.Errorf("event_notify: failed to marshal notice: %v", err) - return nil - } - - return stdinBytes -} diff --git a/src/server/engine/notify_maintainer.go b/src/server/engine/notify_maintainer.go deleted file mode 100644 index b1a86db73422f32aa5569593e1e4ef445ad46727..0000000000000000000000000000000000000000 --- a/src/server/engine/notify_maintainer.go +++ /dev/null @@ -1,70 +0,0 @@ -package engine - -import ( - "encoding/json" - "time" - - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/notifier" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" -) - -type MaintainMessage struct { - Tos []*models.User `json:"tos"` - Title string `json:"title"` - Content string `json:"content"` -} - -// notify to maintainer to handle the error -func notifyToMaintainer(title, msg string) { - logger.Errorf("notifyToMaintainer, msg: %s", msg) - - users := memsto.UserCache.GetMaintainerUsers() - if len(users) == 0 { - return - } - - triggerTime := time.Now().Format("2006/01/02 - 15:04:05") - msg = "Title: " + title + "\nContent: " + msg + "\nTime: " + triggerTime - - notifyMaintainerWithPlugin(title, msg, users) - notifyMaintainerWithBuiltin(title, msg, users) -} - -func notifyMaintainerWithPlugin(title, msg string, users []*models.User) { - if !config.C.Alerting.CallPlugin.Enable { - return - } - - stdinBytes, err := json.Marshal(MaintainMessage{ - Tos: users, - Title: title, - Content: msg, - }) - - if err != nil { - logger.Error("failed to marshal MaintainMessage:", err) - return - } - - notifier.Instance.NotifyMaintainer(stdinBytes) - logger.Debugf("notify maintainer with plugin done") -} - -func notifyMaintainerWithBuiltin(title, msg string, users []*models.User) { - subscription := NewSubscriptionFromUsers(users) - for channel, uids := range subscription.ToChannelUserMap() { - currentUsers := memsto.UserCache.GetByUserIds(uids) - rwLock.RLock() - s := Senders[channel] - rwLock.RUnlock() - if s == nil { - logger.Warningf("no sender for channel: %s", channel) - continue - } - go s.SendRaw(currentUsers, title, msg) - } -} diff --git a/src/server/engine/reporter.go b/src/server/engine/reporter.go deleted file mode 100644 index 3b8fcf335c3891f86f7610f1f3fe2ab8f8518399..0000000000000000000000000000000000000000 --- a/src/server/engine/reporter.go +++ /dev/null @@ -1,65 +0,0 @@ -package engine - -import ( - "sync" - "time" -) - -type ErrorType string - -// register new error here -const ( - QueryPrometheusError ErrorType = "QueryPrometheusError" - RuntimeError ErrorType = "RuntimeError" -) - -type reporter struct { - sync.Mutex - em map[ErrorType]uint64 - cb func(em map[ErrorType]uint64) -} - -var rp reporter - -func initReporter(cb func(em map[ErrorType]uint64)) { - rp = reporter{cb: cb, em: make(map[ErrorType]uint64)} - rp.Start() -} - -func Report(errorType ErrorType) { - rp.report(errorType) -} - -func (r *reporter) reset() map[ErrorType]uint64 { - r.Lock() - defer r.Unlock() - if len(r.em) == 0 { - return nil - } - - oem := r.em - r.em = make(map[ErrorType]uint64) - return oem -} - -func (r *reporter) report(errorType ErrorType) { - r.Lock() - defer r.Unlock() - if count, has := r.em[errorType]; has { - r.em[errorType] = count + 1 - } else { - r.em[errorType] = 1 - } -} - -func (r *reporter) Start() { - for { - select { - case <-time.After(time.Minute): - cur := r.reset() - if cur != nil { - r.cb(cur) - } - } - } -} diff --git a/src/server/engine/route_strategy.go b/src/server/engine/route_strategy.go deleted file mode 100644 index 32d99a53d828e96b59416581fce7349943c20ab1..0000000000000000000000000000000000000000 --- a/src/server/engine/route_strategy.go +++ /dev/null @@ -1,55 +0,0 @@ -package engine - -import ( - "strconv" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" -) - -// Router 抽象由告警事件到订阅者的路由策略 -// rule: 告警规则 -// event: 告警事件 -// prev: 前一次路由结果, Router的实现可以直接修改prev, 也可以返回一个新的Subscription用于AndMerge/OrMerge -type Router func(rule *models.AlertRule, event *models.AlertCurEvent, prev *Subscription) *Subscription - -// GroupRouter 处理告警规则的组订阅关系 -func GroupRouter(rule *models.AlertRule, event *models.AlertCurEvent, prev *Subscription) *Subscription { - groupIds := make([]int64, 0, len(event.NotifyGroupsJSON)) - for _, groupId := range event.NotifyGroupsJSON { - gid, err := strconv.ParseInt(groupId, 10, 64) - if err != nil { - continue - } - groupIds = append(groupIds, gid) - } - groups := memsto.UserGroupCache.GetByUserGroupIds(groupIds) - subscription := NewSubscription() - for _, group := range groups { - for _, userId := range group.UserIds { - subscription.userMap[userId] = NewNotifyChannels(event.NotifyChannelsJSON) - } - } - return subscription -} - -func GlobalWebhookRouter(rule *models.AlertRule, event *models.AlertCurEvent, prev *Subscription) *Subscription { - conf := config.C.Alerting.Webhook - if !conf.Enable { - return nil - } - subscription := NewSubscription() - subscription.webhooks[conf.Url] = conf - return subscription -} - -func EventCallbacksRouter(rule *models.AlertRule, event *models.AlertCurEvent, prev *Subscription) *Subscription { - for _, c := range event.CallbacksJSON { - if c == "" { - continue - } - prev.callbacks[c] = struct{}{} - } - return nil -} diff --git a/src/server/engine/rule.go b/src/server/engine/rule.go deleted file mode 100644 index 85dc3e77f364977c4b099a5a68357aae147d604f..0000000000000000000000000000000000000000 --- a/src/server/engine/rule.go +++ /dev/null @@ -1,169 +0,0 @@ -package engine - -import ( - "context" - "fmt" - "strings" - "sync" - "time" - - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/didi/nightingale/v5/src/server/naming" -) - -// RuleContext is the interface for alert rule and record rule -type RuleContext interface { - Key() string - Hash() string - Prepare() - Start() - Eval() - Stop() -} - -var ruleHolder = &RuleHolder{ - alertRules: make(map[string]RuleContext), - recordRules: make(map[string]RuleContext), - externalAlertRules: make(map[string]*AlertRuleContext), -} - -// RuleHolder is the global rule holder -type RuleHolder struct { - // key: hash - alertRules map[string]RuleContext - recordRules map[string]RuleContext - - // key: key of rule - externalLock sync.RWMutex - externalAlertRules map[string]*AlertRuleContext -} - -func (rh *RuleHolder) LoopSyncRules(ctx context.Context) { - time.Sleep(time.Duration(config.C.EngineDelay) * time.Second) - duration := 9000 * time.Millisecond - for { - select { - case <-ctx.Done(): - return - case <-time.After(duration): - rh.SyncAlertRules() - rh.SyncRecordRules() - } - } -} - -func (rh *RuleHolder) SyncAlertRules() { - ids := memsto.AlertRuleCache.GetRuleIds() - alertRules := make(map[string]RuleContext) - externalAllRules := make(map[string]*AlertRuleContext) - for _, id := range ids { - rule := memsto.AlertRuleCache.Get(id) - if rule == nil { - continue - } - - ruleClusters := config.ReaderClients.Hit(rule.Cluster) - if !rule.IsPrometheusRule() { - // 非 Prometheus 的规则, 不支持 $all, 直接从 rule.Cluster 解析 - ruleClusters = strings.Fields(rule.Cluster) - } - - for _, cluster := range ruleClusters { - // hash ring not hit - if !naming.ClusterHashRing.IsHit(cluster, fmt.Sprintf("%d", rule.Id), config.C.Heartbeat.Endpoint) { - continue - } - - if rule.IsPrometheusRule() { - // 正常的告警规则 - alertRule := NewAlertRuleContext(rule, cluster) - alertRules[alertRule.Hash()] = alertRule - } else { - // 如果 rule 不是通过 prometheus engine 来告警的,则创建为 externalRule - externalRule := NewAlertRuleContext(rule, cluster) - externalAllRules[externalRule.Key()] = externalRule - } - } - } - - for hash, rule := range alertRules { - if _, has := rh.alertRules[hash]; !has { - rule.Prepare() - rule.Start() - rh.alertRules[hash] = rule - } - } - - for hash, rule := range rh.alertRules { - if _, has := alertRules[hash]; !has { - rule.Stop() - delete(rh.alertRules, hash) - } - } - - rh.externalLock.Lock() - for key, rule := range externalAllRules { - if curRule, has := rh.externalAlertRules[key]; has { - // rule存在,且hash一致,认为没有变更,这里可以根据需求单独实现一个关联数据更多的hash函数 - if rule.Hash() == curRule.Hash() { - continue - } - } - // 现有规则中没有rule以及有rule但hash不一致的场景,需要触发rule的update - rule.Prepare() - rh.externalAlertRules[key] = rule - } - - for key := range rh.externalAlertRules { - if _, has := externalAllRules[key]; !has { - delete(rh.externalAlertRules, key) - } - } - rh.externalLock.Unlock() -} - -func (rh *RuleHolder) SyncRecordRules() { - ids := memsto.RecordingRuleCache.GetRuleIds() - recordRules := make(map[string]RuleContext) - for _, id := range ids { - rule := memsto.RecordingRuleCache.Get(id) - if rule == nil { - continue - } - ruleClusters := config.ReaderClients.Hit(rule.Cluster) - for _, cluster := range ruleClusters { - if !naming.ClusterHashRing.IsHit(cluster, fmt.Sprintf("%d", rule.Id), config.C.Heartbeat.Endpoint) { - continue - } - recordRule := NewRecordRuleContext(rule, cluster) - recordRules[recordRule.Hash()] = recordRule - } - } - - for hash, rule := range recordRules { - if _, has := rh.recordRules[hash]; !has { - rule.Prepare() - rule.Start() - rh.recordRules[hash] = rule - } - } - - for hash, rule := range rh.recordRules { - if _, has := recordRules[hash]; !has { - rule.Stop() - delete(rh.recordRules, hash) - } - } -} - -func GetExternalAlertRule(cluster string, id int64) (*AlertRuleContext, bool) { - ruleHolder.externalLock.RLock() - defer ruleHolder.externalLock.RUnlock() - rule, has := ruleHolder.externalAlertRules[ruleKey(cluster, id)] - return rule, has -} - -func ruleKey(cluster string, id int64) string { - return fmt.Sprintf("alert-%s-%d", cluster, id) -} diff --git a/src/server/engine/rule_alert.go b/src/server/engine/rule_alert.go deleted file mode 100644 index 1c15d75571b2045ca722ef2800ea0bac1d509ab6..0000000000000000000000000000000000000000 --- a/src/server/engine/rule_alert.go +++ /dev/null @@ -1,294 +0,0 @@ -package engine - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/prometheus/common/model" - "github.com/toolkits/pkg/logger" - "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/prom" - "github.com/didi/nightingale/v5/src/server/common/conv" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" - promstat "github.com/didi/nightingale/v5/src/server/stat" -) - -type AlertRuleContext struct { - cluster string - quit chan struct{} - - rule *models.AlertRule - fires *AlertCurEventMap - pendings *AlertCurEventMap -} - -func NewAlertRuleContext(rule *models.AlertRule, cluster string) *AlertRuleContext { - return &AlertRuleContext{ - cluster: cluster, - quit: make(chan struct{}), - rule: rule, - } -} - -func (arc *AlertRuleContext) RuleFromCache() *models.AlertRule { - return memsto.AlertRuleCache.Get(arc.rule.Id) -} - -func (arc *AlertRuleContext) Key() string { - return ruleKey(arc.cluster, arc.rule.Id) -} - -func (arc *AlertRuleContext) Hash() string { - return str.MD5(fmt.Sprintf("%d_%d_%s_%s", - arc.rule.Id, - arc.rule.PromEvalInterval, - arc.rule.PromQl, - arc.cluster, - )) -} - -func (arc *AlertRuleContext) Prepare() { - arc.recoverAlertCurEventFromDb() -} - -func (arc *AlertRuleContext) Start() { - logger.Infof("eval:%s started", arc.Key()) - interval := arc.rule.PromEvalInterval - if interval <= 0 { - interval = 10 - } - go func() { - for { - select { - case <-arc.quit: - return - default: - arc.Eval() - time.Sleep(time.Duration(interval) * time.Second) - } - } - }() -} - -func (arc *AlertRuleContext) Eval() { - promql := strings.TrimSpace(arc.rule.PromQl) - if promql == "" { - logger.Errorf("rule_eval:%s promql is blank", arc.Key()) - return - } - - if config.ReaderClients.IsNil(arc.cluster) { - logger.Errorf("rule_eval:%s error reader client is nil", arc.Key()) - return - } - - readerClient := config.ReaderClients.GetCli(arc.cluster) - - var value model.Value - var err error - - cachedRule := arc.RuleFromCache() - if cachedRule == nil { - logger.Errorf("rule_eval:%s rule not found", arc.Key()) - return - } - - // 如果是单个goroutine执行, 完全可以考虑把cachedRule赋值给arc.rule, 不会有问题 - // 但是在externalRule的场景中, 会调用HandleVectors/RecoverSingle;就行不通了,还是在需要的时候从cache中拿rule吧 - // arc.rule = cachedRule - - // 如果cache中的规则由prometheus规则改为其他类型,也没必要再去prometheus查询了 - if cachedRule.IsPrometheusRule() { - var warnings prom.Warnings - value, warnings, err = readerClient.Query(context.Background(), promql, time.Now()) - if err != nil { - logger.Errorf("rule_eval:%s promql:%s, error:%v", arc.Key(), promql, err) - //notifyToMaintainer(err, "failed to query prometheus") - Report(QueryPrometheusError) - return - } - - if len(warnings) > 0 { - logger.Errorf("rule_eval:%s promql:%s, warnings:%v", arc.Key(), promql, warnings) - return - } - logger.Debugf("rule_eval:%s promql:%s, value:%v", arc.Key(), promql, value) - } - arc.HandleVectors(conv.ConvertVectors(value), "inner") -} - -func (arc *AlertRuleContext) HandleVectors(vectors []conv.Vector, from string) { - // 有可能rule的一些配置已经发生变化,比如告警接收人、callbacks等 - // 这些信息的修改是不会引起worker restart的,但是确实会影响告警处理逻辑 - // 所以,这里直接从memsto.AlertRuleCache中获取并覆盖 - cachedRule := arc.RuleFromCache() - if cachedRule == nil { - logger.Errorf("rule_eval:%s rule not found", arc.Key()) - return - } - now := time.Now().Unix() - alertingKeys := map[string]struct{}{} - for _, vector := range vectors { - alertVector := NewAlertVector(arc, cachedRule, vector, from) - event := alertVector.BuildEvent(now) - // 如果event被mute了,本质也是fire的状态,这里无论如何都添加到alertingKeys中,防止fire的事件自动恢复了 - alertingKeys[alertVector.Hash()] = struct{}{} - if IsMuted(cachedRule, event) { - continue - } - arc.handleEvent(event) - } - - arc.HandleRecover(alertingKeys, now) -} - -func (arc *AlertRuleContext) HandleRecover(alertingKeys map[string]struct{}, now int64) { - for _, hash := range arc.pendings.Keys() { - if _, has := alertingKeys[hash]; has { - continue - } - arc.pendings.Delete(hash) - } - - for hash := range arc.fires.GetAll() { - if _, has := alertingKeys[hash]; has { - continue - } - arc.RecoverSingle(hash, now, nil) - } -} - -func (arc *AlertRuleContext) RecoverSingle(hash string, now int64, value *string) { - cachedRule := arc.RuleFromCache() - if cachedRule == nil { - return - } - event, has := arc.fires.Get(hash) - if !has { - return - } - // 如果配置了留观时长,就不能立马恢复了 - if cachedRule.RecoverDuration > 0 && now-event.LastEvalTime < cachedRule.RecoverDuration { - return - } - if value != nil { - event.TriggerValue = *value - } - - // 没查到触发阈值的vector,姑且就认为这个vector的值恢复了 - // 我确实无法分辨,是prom中有值但是未满足阈值所以没返回,还是prom中确实丢了一些点导致没有数据可以返回,尴尬 - arc.fires.Delete(hash) - arc.pendings.Delete(hash) - - // 可能是因为调整了promql才恢复的,所以事件里边要体现最新的promql,否则用户会比较困惑 - // 当然,其实rule的各个字段都可能发生变化了,都更新一下吧 - cachedRule.UpdateEvent(event) - event.IsRecovered = true - event.LastEvalTime = now - arc.pushEventToQueue(event) -} - -func (arc *AlertRuleContext) handleEvent(event *models.AlertCurEvent) { - if event == nil { - return - } - if event.PromForDuration == 0 { - arc.fireEvent(event) - return - } - - var preTriggerTime int64 - preEvent, has := arc.pendings.Get(event.Hash) - if has { - arc.pendings.UpdateLastEvalTime(event.Hash, event.LastEvalTime) - preTriggerTime = preEvent.TriggerTime - } else { - arc.pendings.Set(event.Hash, event) - preTriggerTime = event.TriggerTime - } - - if event.LastEvalTime-preTriggerTime+int64(event.PromEvalInterval) >= int64(event.PromForDuration) { - arc.fireEvent(event) - } -} - -func (arc *AlertRuleContext) fireEvent(event *models.AlertCurEvent) { - // As arc.rule maybe outdated, use rule from cache - cachedRule := arc.RuleFromCache() - if cachedRule == nil { - return - } - if fired, has := arc.fires.Get(event.Hash); has { - arc.fires.UpdateLastEvalTime(event.Hash, event.LastEvalTime) - - if cachedRule.NotifyRepeatStep == 0 { - // 说明不想重复通知,那就直接返回了,nothing to do - return - } - - // 之前发送过告警了,这次是否要继续发送,要看是否过了通道静默时间 - if event.LastEvalTime > fired.LastSentTime+int64(cachedRule.NotifyRepeatStep)*60 { - if cachedRule.NotifyMaxNumber == 0 { - // 最大可以发送次数如果是0,表示不想限制最大发送次数,一直发即可 - event.NotifyCurNumber = fired.NotifyCurNumber + 1 - event.FirstTriggerTime = fired.FirstTriggerTime - arc.pushEventToQueue(event) - } else { - // 有最大发送次数的限制,就要看已经发了几次了,是否达到了最大发送次数 - if fired.NotifyCurNumber >= cachedRule.NotifyMaxNumber { - return - } else { - event.NotifyCurNumber = fired.NotifyCurNumber + 1 - event.FirstTriggerTime = fired.FirstTriggerTime - arc.pushEventToQueue(event) - } - } - } - } else { - event.NotifyCurNumber = 1 - event.FirstTriggerTime = event.TriggerTime - arc.pushEventToQueue(event) - } -} - -func (arc *AlertRuleContext) pushEventToQueue(event *models.AlertCurEvent) { - if !event.IsRecovered { - event.LastSentTime = event.LastEvalTime - arc.fires.Set(event.Hash, event) - } - - promstat.CounterAlertsTotal.WithLabelValues(event.Cluster).Inc() - LogEvent(event, "push_queue") - if !EventQueue.PushFront(event) { - logger.Warningf("event_push_queue: queue is full, event:%+v", event) - } -} - -func (arc *AlertRuleContext) Stop() { - logger.Infof("%s stopped", arc.Key()) - close(arc.quit) -} - -func (arc *AlertRuleContext) recoverAlertCurEventFromDb() { - arc.pendings = NewAlertCurEventMap(nil) - - curEvents, err := models.AlertCurEventGetByRuleIdAndCluster(arc.rule.Id, arc.cluster) - if err != nil { - logger.Errorf("recover event from db for rule:%s failed, err:%s", arc.Key(), err) - arc.fires = NewAlertCurEventMap(nil) - return - } - - fireMap := make(map[string]*models.AlertCurEvent) - for _, event := range curEvents { - event.DB2Mem() - fireMap[event.Hash] = event - } - - arc.fires = NewAlertCurEventMap(fireMap) -} diff --git a/src/server/engine/rule_helper.go b/src/server/engine/rule_helper.go deleted file mode 100644 index ade24b8d48da701a0b9f942686c94d575ff47804..0000000000000000000000000000000000000000 --- a/src/server/engine/rule_helper.go +++ /dev/null @@ -1,189 +0,0 @@ -package engine - -import ( - "fmt" - "sort" - "strings" - "sync" - - "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/common/conv" - "github.com/didi/nightingale/v5/src/server/memsto" -) - -type AlertCurEventMap struct { - sync.RWMutex - Data map[string]*models.AlertCurEvent -} - -func (a *AlertCurEventMap) SetAll(data map[string]*models.AlertCurEvent) { - a.Lock() - defer a.Unlock() - a.Data = data -} - -func (a *AlertCurEventMap) Set(key string, value *models.AlertCurEvent) { - a.Lock() - defer a.Unlock() - a.Data[key] = value -} - -func (a *AlertCurEventMap) Get(key string) (*models.AlertCurEvent, bool) { - a.RLock() - defer a.RUnlock() - event, exists := a.Data[key] - return event, exists -} - -func (a *AlertCurEventMap) UpdateLastEvalTime(key string, lastEvalTime int64) { - a.Lock() - defer a.Unlock() - event, exists := a.Data[key] - if !exists { - return - } - event.LastEvalTime = lastEvalTime -} - -func (a *AlertCurEventMap) Delete(key string) { - a.Lock() - defer a.Unlock() - delete(a.Data, key) -} - -func (a *AlertCurEventMap) Keys() []string { - a.RLock() - defer a.RUnlock() - keys := make([]string, 0, len(a.Data)) - for k := range a.Data { - keys = append(keys, k) - } - return keys -} - -func (a *AlertCurEventMap) GetAll() map[string]*models.AlertCurEvent { - a.RLock() - defer a.RUnlock() - return a.Data -} - -func NewAlertCurEventMap(data map[string]*models.AlertCurEvent) *AlertCurEventMap { - if data == nil { - return &AlertCurEventMap{ - Data: make(map[string]*models.AlertCurEvent), - } - } - return &AlertCurEventMap{ - Data: data, - } -} - -// AlertVector 包含一个告警事件的告警上下文 -type AlertVector struct { - Ctx *AlertRuleContext - Rule *models.AlertRule - Vector conv.Vector - From string - - tagsMap map[string]string - tagsArr []string - target string - targetNote string - groupName string -} - -func NewAlertVector(ctx *AlertRuleContext, rule *models.AlertRule, vector conv.Vector, from string) *AlertVector { - if rule == nil { - rule = ctx.rule - } - av := &AlertVector{ - Ctx: ctx, - Rule: rule, - Vector: vector, - From: from, - } - av.fillTags() - av.mayHandleIdent() - av.mayHandleGroup() - return av -} - -func (av *AlertVector) Hash() string { - return str.MD5(fmt.Sprintf("%d_%s_%s", av.Rule.Id, av.Vector.Key, av.Ctx.cluster)) -} - -func (av *AlertVector) fillTags() { - // handle series tags - tagsMap := make(map[string]string) - for label, value := range av.Vector.Labels { - tagsMap[string(label)] = string(value) - } - - // handle rule tags - for _, tag := range av.Rule.AppendTagsJSON { - arr := strings.SplitN(tag, "=", 2) - tagsMap[arr[0]] = arr[1] - } - - tagsMap["rulename"] = av.Rule.Name - av.tagsMap = tagsMap - - // handle tagsArr - av.tagsArr = labelMapToArr(tagsMap) -} - -func (av *AlertVector) mayHandleIdent() { - // handle ident - if ident, has := av.tagsMap["ident"]; has { - if target, exists := memsto.TargetCache.Get(ident); exists { - av.target = target.Ident - av.targetNote = target.Note - } - } -} - -func (av *AlertVector) mayHandleGroup() { - // handle bg - bg := memsto.BusiGroupCache.GetByBusiGroupId(av.Rule.GroupId) - if bg != nil { - av.groupName = bg.Name - } -} - -func (av *AlertVector) BuildEvent(now int64) *models.AlertCurEvent { - event := av.Rule.GenerateNewEvent() - event.TriggerTime = av.Vector.Timestamp - event.TagsMap = av.tagsMap - event.Cluster = av.Ctx.cluster - event.Hash = av.Hash() - event.TargetIdent = av.target - event.TargetNote = av.targetNote - event.TriggerValue = av.Vector.ReadableValue() - event.TagsJSON = av.tagsArr - event.GroupName = av.groupName - event.Tags = strings.Join(av.tagsArr, ",,") - event.IsRecovered = false - - if av.From == "inner" { - event.LastEvalTime = now - } else { - event.LastEvalTime = event.TriggerTime - } - return event -} - -func labelMapToArr(m map[string]string) []string { - numLabels := len(m) - - labelStrings := make([]string, 0, numLabels) - for label, value := range m { - labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", label, value)) - } - - if numLabels > 1 { - sort.Strings(labelStrings) - } - return labelStrings -} diff --git a/src/server/engine/subscription.go b/src/server/engine/subscription.go deleted file mode 100644 index 5a7eae1fb1feb29df32ae1d947c78dbdd6a152bd..0000000000000000000000000000000000000000 --- a/src/server/engine/subscription.go +++ /dev/null @@ -1,139 +0,0 @@ -package engine - -import ( - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" -) - -// NotifyChannels channelKey -> bool -type NotifyChannels map[string]bool - -func NewNotifyChannels(channels []string) NotifyChannels { - nc := make(NotifyChannels) - for _, ch := range channels { - nc[ch] = true - } - return nc -} - -func (nc NotifyChannels) OrMerge(other NotifyChannels) { - nc.merge(other, func(a, b bool) bool { return a || b }) -} - -func (nc NotifyChannels) AndMerge(other NotifyChannels) { - nc.merge(other, func(a, b bool) bool { return a && b }) -} - -func (nc NotifyChannels) merge(other NotifyChannels, f func(bool, bool) bool) { - if other == nil { - return - } - for k, v := range other { - if curV, has := nc[k]; has { - nc[k] = f(curV, v) - } else { - nc[k] = v - } - } -} - -// Subscription 维护所有需要发送的用户-通道/回调/钩子信息,用map维护的数据结构具有去重功能 -type Subscription struct { - userMap map[int64]NotifyChannels - webhooks map[string]config.Webhook - callbacks map[string]struct{} -} - -func NewSubscription() *Subscription { - return &Subscription{ - userMap: make(map[int64]NotifyChannels), - webhooks: make(map[string]config.Webhook), - callbacks: make(map[string]struct{}), - } -} - -// NewSubscriptionFromUsers 根据用户的token配置,生成订阅信息,用于notifyMaintainer -func NewSubscriptionFromUsers(users []*models.User) *Subscription { - s := NewSubscription() - for _, u := range users { - if u == nil { - continue - } - for channel, token := range u.ExtractAllToken() { - if token == "" { - continue - } - if channelMap, has := s.userMap[u.Id]; has { - channelMap[channel] = true - } else { - s.userMap[u.Id] = map[string]bool{ - channel: true, - } - } - } - } - return s -} - -// OrMerge 将channelMap按照or的方式合并,方便实现多种组合的策略,比如根据某个tag进行路由等 -func (s *Subscription) OrMerge(other *Subscription) { - s.merge(other, NotifyChannels.OrMerge) -} - -// AndMerge 将channelMap中的bool值按照and的逻辑进行合并,可以单独将人/通道维度的通知移除 -// 常用的场景有: -// 1. 人员离职了不需要发送告警了 -// 2. 某个告警通道进行维护,暂时不需要发送告警了 -// 3. 业务值班的重定向逻辑,将高等级的告警额外发送给应急人员等 -// 可以结合业务需求自己实现router -func (s *Subscription) AndMerge(other *Subscription) { - s.merge(other, NotifyChannels.AndMerge) -} - -func (s *Subscription) merge(other *Subscription, f func(NotifyChannels, NotifyChannels)) { - if other == nil { - return - } - for k, v := range other.userMap { - if curV, has := s.userMap[k]; has { - f(curV, v) - } else { - s.userMap[k] = v - } - } - for k, v := range other.webhooks { - s.webhooks[k] = v - } - for k, v := range other.callbacks { - s.callbacks[k] = v - } -} - -// ToChannelUserMap userMap(map[uid][channel]bool) 转换为 map[channel][]uid 的结构 -func (s *Subscription) ToChannelUserMap() map[string][]int64 { - m := make(map[string][]int64) - for uid, nc := range s.userMap { - for ch, send := range nc { - if send { - m[ch] = append(m[ch], uid) - } - } - } - return m -} - -func (s *Subscription) ToCallbackList() []string { - callbacks := make([]string, 0, len(s.callbacks)) - for cb := range s.callbacks { - callbacks = append(callbacks, cb) - } - return callbacks -} - -func (s *Subscription) ToWebhookList() []config.Webhook { - webhooks := make([]config.Webhook, 0, len(s.webhooks)) - for _, wh := range s.webhooks { - webhooks = append(webhooks, wh) - } - return webhooks -} diff --git a/src/server/idents/idents.go b/src/server/idents/idents.go deleted file mode 100644 index 1df865b3a8fd9da2a5921b5c97cac99b270e0510..0000000000000000000000000000000000000000 --- a/src/server/idents/idents.go +++ /dev/null @@ -1,206 +0,0 @@ -package idents - -import ( - "context" - "fmt" - "strconv" - "time" - - cmap "github.com/orcaman/concurrent-map" - "github.com/prometheus/common/model" - "github.com/prometheus/prometheus/prompb" - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/common" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/didi/nightingale/v5/src/server/naming" - "github.com/didi/nightingale/v5/src/server/writer" - "github.com/didi/nightingale/v5/src/storage" -) - -// ident -> timestamp -var Idents = cmap.New() - -func loopToRedis(ctx context.Context) { - duration := time.Duration(4) * time.Second - for { - select { - case <-ctx.Done(): - return - case <-time.After(duration): - toRedis() - } - } -} - -func toRedis() { - items := Idents.Items() - if len(items) == 0 { - return - } - - if config.ReaderClients.IsNil(config.C.ClusterName) { - return - } - - now := time.Now().Unix() - - // clean old idents - for key, at := range items { - if at.(int64) < now-config.C.NoData.Interval { - Idents.Remove(key) - } else { - // use now as timestamp to redis - err := storage.Redis.HSet(context.Background(), redisKey(config.C.ClusterName), key, now).Err() - if err != nil { - logger.Errorf("redis hset idents failed: %v", err) - } - } - } -} - -// hash struct: -// /idents/Default -> { -// $ident => $timestamp -// $ident => $timestamp -// } -func redisKey(cluster string) string { - return fmt.Sprintf("/idents/%s", cluster) -} - -func clearDeadIdent(ctx context.Context, cluster, ident string) { - key := redisKey(cluster) - err := storage.Redis.HDel(ctx, key, ident).Err() - if err != nil { - logger.Warningf("failed to hdel %s %s, error: %v", key, ident, err) - } -} - -func Handle(ctx context.Context) { - go loopToRedis(ctx) - go loopPushMetrics(ctx) -} - -func loopPushMetrics(ctx context.Context) { - duration := time.Duration(10) * time.Second - for { - select { - case <-ctx.Done(): - return - case <-time.After(duration): - pushMetrics() - } - } -} - -func pushMetrics() { - clusterName := config.C.ClusterName - isLeader, err := naming.IamLeader(clusterName) - if err != nil { - logger.Errorf("handle_idents: %v", err) - return - } - - if !isLeader { - logger.Info("handle_idents: i am not leader") - return - } - - // get all the target heartbeat timestamp - ret, err := storage.Redis.HGetAll(context.Background(), redisKey(clusterName)).Result() - if err != nil { - logger.Errorf("handle_idents: redis hgetall fail: %v", err) - return - } - - now := time.Now().Unix() - dur := config.C.NoData.Interval - - actives := make(map[string]struct{}) - for ident, clockstr := range ret { - clock, err := strconv.ParseInt(clockstr, 10, 64) - if err != nil { - continue - } - - if now-clock > dur { - clearDeadIdent(context.Background(), clusterName, ident) - } else { - actives[ident] = struct{}{} - } - } - - // 有心跳,target_up = 1 - // 如果找到target,就把target的tags补充到series上 - // 如果没有target,就在数据库创建target - for active := range actives { - // build metrics - pt := &prompb.TimeSeries{} - pt.Samples = append(pt.Samples, prompb.Sample{ - // use ms - Timestamp: now * 1000, - Value: 1, - }) - - pt.Labels = append(pt.Labels, &prompb.Label{ - Name: model.MetricNameLabel, - Value: config.C.NoData.Metric, - }) - - pt.Labels = append(pt.Labels, &prompb.Label{ - Name: "ident", - Value: active, - }) - - target, has := memsto.TargetCache.Get(active) - if !has { - // target not exists - target = &models.Target{ - Cluster: clusterName, - Ident: active, - Tags: "", - TagsJSON: []string{}, - TagsMap: make(map[string]string), - UpdateAt: now, - } - - if err := target.Add(); err != nil { - logger.Errorf("handle_idents: insert target(%s) fail: %v", active, err) - } - } else { - common.AppendLabels(pt, target) - } - - writer.Writers.PushSample("target_up", pt) - } - - // 把actives传给TargetCache,看看除了active的部分,还有别的target么?有的话返回,设置target_up = 0 - deads := memsto.TargetCache.GetDeads(actives) - for ident, dead := range deads { - if ident == "" { - continue - } - // build metrics - pt := &prompb.TimeSeries{} - pt.Samples = append(pt.Samples, prompb.Sample{ - // use ms - Timestamp: now * 1000, - Value: 0, - }) - - pt.Labels = append(pt.Labels, &prompb.Label{ - Name: model.MetricNameLabel, - Value: config.C.NoData.Metric, - }) - - pt.Labels = append(pt.Labels, &prompb.Label{ - Name: "ident", - Value: ident, - }) - - common.AppendLabels(pt, dead) - writer.Writers.PushSample("target_up", pt) - } -} diff --git a/src/server/memsto/busi_group_cache.go b/src/server/memsto/busi_group_cache.go deleted file mode 100644 index 5d2c6b8fc3d7212e232d787e7955cffe9d1b58b0..0000000000000000000000000000000000000000 --- a/src/server/memsto/busi_group_cache.go +++ /dev/null @@ -1,103 +0,0 @@ -package memsto - -import ( - "fmt" - "sync" - "time" - - "github.com/pkg/errors" - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - promstat "github.com/didi/nightingale/v5/src/server/stat" -) - -type BusiGroupCacheType struct { - statTotal int64 - statLastUpdated int64 - - sync.RWMutex - ugs map[int64]*models.BusiGroup // key: id -} - -var BusiGroupCache = BusiGroupCacheType{ - statTotal: -1, - statLastUpdated: -1, - ugs: make(map[int64]*models.BusiGroup), -} - -func (c *BusiGroupCacheType) StatChanged(total, lastUpdated int64) bool { - if c.statTotal == total && c.statLastUpdated == lastUpdated { - return false - } - - return true -} - -func (c *BusiGroupCacheType) Set(ugs map[int64]*models.BusiGroup, total, lastUpdated int64) { - c.Lock() - c.ugs = ugs - c.Unlock() - - // only one goroutine used, so no need lock - c.statTotal = total - c.statLastUpdated = lastUpdated -} - -func (c *BusiGroupCacheType) GetByBusiGroupId(id int64) *models.BusiGroup { - c.RLock() - defer c.RUnlock() - return c.ugs[id] -} - -func SyncBusiGroups() { - err := syncBusiGroups() - if err != nil { - fmt.Println("failed to sync busi groups:", err) - exit(1) - } - - go loopSyncBusiGroups() -} - -func loopSyncBusiGroups() { - duration := time.Duration(9000) * time.Millisecond - for { - time.Sleep(duration) - if err := syncBusiGroups(); err != nil { - logger.Warning("failed to sync busi groups:", err) - } - } -} - -func syncBusiGroups() error { - start := time.Now() - - stat, err := models.BusiGroupStatistics() - if err != nil { - return errors.WithMessage(err, "failed to exec BusiGroupStatistics") - } - - if !BusiGroupCache.StatChanged(stat.Total, stat.LastUpdated) { - promstat.GaugeCronDuration.WithLabelValues("sync_busi_groups").Set(0) - promstat.GaugeSyncNumber.WithLabelValues("sync_busi_groups").Set(0) - - logger.Debug("busi_group not changed") - return nil - } - - m, err := models.BusiGroupGetMap() - if err != nil { - return errors.WithMessage(err, "failed to exec BusiGroupGetMap") - } - - BusiGroupCache.Set(m, stat.Total, stat.LastUpdated) - - ms := time.Since(start).Milliseconds() - promstat.GaugeCronDuration.WithLabelValues("sync_busi_groups").Set(float64(ms)) - promstat.GaugeSyncNumber.WithLabelValues("sync_busi_groups").Set(float64(len(m))) - - logger.Infof("timer: sync busi groups done, cost: %dms, number: %d", ms, len(m)) - - return nil -} diff --git a/src/server/memsto/log_sample_filter_cache.go b/src/server/memsto/log_sample_filter_cache.go deleted file mode 100644 index 884f4b9044b3a17d52cb45f6471f84970477233b..0000000000000000000000000000000000000000 --- a/src/server/memsto/log_sample_filter_cache.go +++ /dev/null @@ -1,44 +0,0 @@ -package memsto - -import ( - "sync" -) - -type LogSampleCacheType struct { - sync.RWMutex - m map[string]map[string]struct{} // map[labelName]map[labelValue]struct{} -} - -var LogSampleCache = LogSampleCacheType{ - m: make(map[string]map[string]struct{}), -} - -func (l *LogSampleCacheType) Set(m map[string][]string) { - l.Lock() - for k, v := range m { - l.m[k] = make(map[string]struct{}) - for _, vv := range v { - l.m[k][vv] = struct{}{} - } - } - l.Unlock() -} - -func (l *LogSampleCacheType) Get() map[string]map[string]struct{} { - l.RLock() - defer l.RUnlock() - - return l.m -} - -func (l *LogSampleCacheType) Clean() { - l.Lock() - l.m = make(map[string]map[string]struct{}) - l.Unlock() -} - -func (l *LogSampleCacheType) Len() int { - l.RLock() - defer l.RUnlock() - return len(l.m) -} diff --git a/src/server/memsto/memsto.go b/src/server/memsto/memsto.go deleted file mode 100644 index 027eab4f61f5305f8f5bdea58afbbca6a0842765..0000000000000000000000000000000000000000 --- a/src/server/memsto/memsto.go +++ /dev/null @@ -1,23 +0,0 @@ -package memsto - -import ( - "os" - - "github.com/toolkits/pkg/logger" -) - -func exit(code int) { - logger.Close() - os.Exit(code) -} - -func Sync() { - SyncBusiGroups() - SyncUsers() - SyncUserGroups() - SyncAlertMutes() - SyncAlertSubscribes() - SyncAlertRules() - SyncTargets() - SyncRecordingRules() -} diff --git a/src/server/naming/hashring.go b/src/server/naming/hashring.go deleted file mode 100644 index a02f36c6bf2324398d97aed7372748335daf300d..0000000000000000000000000000000000000000 --- a/src/server/naming/hashring.go +++ /dev/null @@ -1,64 +0,0 @@ -package naming - -import ( - "sync" - - "github.com/toolkits/pkg/consistent" - "github.com/toolkits/pkg/logger" -) - -const NodeReplicas = 500 - -type ClusterHashRingType struct { - sync.RWMutex - Rings map[string]*consistent.Consistent -} - -// for alert_rule sharding -var ClusterHashRing = ClusterHashRingType{Rings: make(map[string]*consistent.Consistent)} - -func NewConsistentHashRing(replicas int32, nodes []string) *consistent.Consistent { - ret := consistent.New() - ret.NumberOfReplicas = int(replicas) - for i := 0; i < len(nodes); i++ { - ret.Add(nodes[i]) - } - return ret -} - -func RebuildConsistentHashRing(cluster string, nodes []string) { - r := consistent.New() - r.NumberOfReplicas = NodeReplicas - for i := 0; i < len(nodes); i++ { - r.Add(nodes[i]) - } - - ClusterHashRing.Set(cluster, r) - logger.Infof("hash ring %s rebuild %+v", cluster, r.Members()) -} - -func (chr *ClusterHashRingType) GetNode(cluster, pk string) (string, error) { - chr.RLock() - defer chr.RUnlock() - _, exists := chr.Rings[cluster] - if !exists { - chr.Rings[cluster] = NewConsistentHashRing(int32(NodeReplicas), []string{}) - } - - return chr.Rings[cluster].Get(pk) -} - -func (chr *ClusterHashRingType) IsHit(cluster string, pk string, currentNode string) bool { - node, err := chr.GetNode(cluster, pk) - if err != nil { - logger.Debugf("cluster:%s pk:%s failed to get node from hashring:%v", cluster, pk, err) - return false - } - return node == currentNode -} - -func (chr *ClusterHashRingType) Set(cluster string, r *consistent.Consistent) { - chr.RLock() - defer chr.RUnlock() - chr.Rings[cluster] = r -} diff --git a/src/server/naming/heartbeat.go b/src/server/naming/heartbeat.go deleted file mode 100644 index f636f767c2e20e1f975dcbf48340292fba667e30..0000000000000000000000000000000000000000 --- a/src/server/naming/heartbeat.go +++ /dev/null @@ -1,103 +0,0 @@ -package naming - -import ( - "context" - "fmt" - "sort" - "strings" - "time" - - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" -) - -// local servers -var localss map[string]string - -func Heartbeat(ctx context.Context) error { - localss = make(map[string]string) - if err := heartbeat(); err != nil { - fmt.Println("failed to heartbeat:", err) - return err - } - - go loopHeartbeat() - return nil -} - -func loopHeartbeat() { - interval := time.Duration(config.C.Heartbeat.Interval) * time.Millisecond - for { - time.Sleep(interval) - if err := heartbeat(); err != nil { - logger.Warning(err) - } - } -} - -func heartbeat() error { - var clusters []string - var err error - if config.C.ReaderFrom == "config" { - // 在配置文件维护实例和集群的对应关系 - for i := 0; i < len(config.C.Readers); i++ { - clusters = append(clusters, config.C.Readers[i].ClusterName) - err := models.AlertingEngineHeartbeatWithCluster(config.C.Heartbeat.Endpoint, config.C.Readers[i].ClusterName) - if err != nil { - logger.Warningf("heartbeat with cluster %s err:%v", config.C.Readers[i].ClusterName, err) - continue - } - } - } else { - // 在页面上维护实例和集群的对应关系 - clusters, err = models.AlertingEngineGetClusters(config.C.Heartbeat.Endpoint) - if err != nil { - return err - } - if len(clusters) == 0 { - // 告警引擎页面还没有配置集群,先上报一个空的集群,让 n9e-server 实例在页面上显示出来 - err := models.AlertingEngineHeartbeatWithCluster(config.C.Heartbeat.Endpoint, "") - if err != nil { - logger.Warningf("heartbeat with cluster %s err:%v", "", err) - } - logger.Warningf("heartbeat %s no cluster", config.C.Heartbeat.Endpoint) - } - - err := models.AlertingEngineHeartbeat(config.C.Heartbeat.Endpoint) - if err != nil { - return err - } - } - - for i := 0; i < len(clusters); i++ { - servers, err := ActiveServers(clusters[i]) - if err != nil { - logger.Warningf("hearbeat %s get active server err:", clusters[i], err) - continue - } - - sort.Strings(servers) - newss := strings.Join(servers, " ") - - oldss, exists := localss[clusters[i]] - if exists && oldss == newss { - continue - } - - RebuildConsistentHashRing(clusters[i], servers) - localss[clusters[i]] = newss - } - - return nil -} - -func ActiveServers(cluster string) ([]string, error) { - if cluster == "" { - return nil, fmt.Errorf("cluster is empty") - } - - // 30秒内有心跳,就认为是活的 - return models.AlertingEngineGetsInstances("cluster = ? and clock > ?", cluster, time.Now().Unix()-30) -} diff --git a/src/server/router/router.go b/src/server/router/router.go deleted file mode 100644 index 0671da3257902880f42a752a88cebc062dbc3a4f..0000000000000000000000000000000000000000 --- a/src/server/router/router.go +++ /dev/null @@ -1,124 +0,0 @@ -package router - -import ( - "fmt" - "os" - "strings" - "time" - - "github.com/gin-contrib/pprof" - "github.com/gin-gonic/gin" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/pkg/aop" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/naming" - promstat "github.com/didi/nightingale/v5/src/server/stat" -) - -func New(version string, reloadFunc func()) *gin.Engine { - gin.SetMode(config.C.RunMode) - - loggerMid := aop.Logger() - recoveryMid := aop.Recovery() - - if strings.ToLower(config.C.RunMode) == "release" { - aop.DisableConsoleColor() - } - - r := gin.New() - - r.Use(recoveryMid) - - // whether print access log - if config.C.HTTP.PrintAccessLog { - r.Use(loggerMid) - } - - configRoute(r, version, reloadFunc) - - return r -} - -func configRoute(r *gin.Engine, version string, reloadFunc func()) { - if config.C.HTTP.PProf { - pprof.Register(r, "/api/debug/pprof") - } - - r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") - }) - - r.GET("/pid", func(c *gin.Context) { - c.String(200, fmt.Sprintf("%d", os.Getpid())) - }) - - r.GET("/addr", func(c *gin.Context) { - c.String(200, c.Request.RemoteAddr) - }) - - r.GET("/version", func(c *gin.Context) { - c.String(200, version) - }) - - r.POST("/-/reload", func(c *gin.Context) { - reloadFunc() - c.String(200, "reload success") - }) - - r.GET("/servers/active", func(c *gin.Context) { - lst, err := naming.ActiveServers(ginx.QueryStr(c, "cluster")) - ginx.NewRender(c).Data(lst, err) - }) - - // use apiKey not basic auth - r.POST("/datadog/api/v1/series", stat(), datadogSeries) - r.POST("/datadog/api/v1/check_run", datadogCheckRun) - r.GET("/datadog/api/v1/validate", datadogValidate) - r.POST("/datadog/api/v1/metadata", datadogMetadata) - r.POST("/datadog/intake/", datadogIntake) - - if len(config.C.BasicAuth) > 0 { - auth := gin.BasicAuth(config.C.BasicAuth) - r.Use(auth) - } - - r.POST("/opentsdb/put", stat(), handleOpenTSDB) - r.POST("/openfalcon/push", stat(), falconPush) - r.POST("/prometheus/v1/write", stat(), remoteWrite) - r.POST("/prometheus/v1/query", stat(), queryPromql) - - r.GET("/memory/alert-rule", alertRuleGet) - r.GET("/memory/alert-rule-location", alertRuleLocationGet) - r.GET("/memory/idents", identsGets) - r.GET("/memory/alert-mutes", mutesGets) - r.GET("/memory/alert-subscribes", subscribesGets) - r.GET("/memory/target", targetGet) - r.GET("/memory/user", userGet) - r.GET("/memory/user-group", userGroupGet) - - r.GET("/metrics", gin.WrapH(promhttp.Handler())) - - r.GET("/log-sample-filter", logSampleFilterGet) - r.POST("/log-sample-filter", logSampleFilterAdd) - r.DELETE("/log-sample-filter", logSampleFilterDel) - - service := r.Group("/v1/n9e") - service.POST("/event", pushEventToQueue) - service.POST("/make-event", makeEvent) - service.POST("/judge-event", judgeEvent) -} - -func stat() gin.HandlerFunc { - return func(c *gin.Context) { - start := time.Now() - c.Next() - - code := fmt.Sprintf("%d", c.Writer.Status()) - method := c.Request.Method - labels := []string{code, c.FullPath(), method} - - promstat.RequestDuration.WithLabelValues(labels...).Observe(time.Since(start).Seconds()) - } -} diff --git a/src/server/router/router_easyjson.go b/src/server/router/router_easyjson.go deleted file mode 100644 index 9971de023f280e2a3965f715bbb2fb45367f6a70..0000000000000000000000000000000000000000 --- a/src/server/router/router_easyjson.go +++ /dev/null @@ -1,18 +0,0 @@ -// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. - -package router - -import ( - json "encoding/json" - easyjson "github.com/mailru/easyjson" - jlexer "github.com/mailru/easyjson/jlexer" - jwriter "github.com/mailru/easyjson/jwriter" -) - -// suppress unused package warning -var ( - _ *json.RawMessage - _ *jlexer.Lexer - _ *jwriter.Writer - _ easyjson.Marshaler -) diff --git a/src/server/router/router_log_sample_filter.go b/src/server/router/router_log_sample_filter.go deleted file mode 100644 index 195bf22880ebd53d972489579c761ffe029cc3c9..0000000000000000000000000000000000000000 --- a/src/server/router/router_log_sample_filter.go +++ /dev/null @@ -1,55 +0,0 @@ -package router - -import ( - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/gin-gonic/gin" - "github.com/prometheus/prometheus/prompb" - "github.com/toolkits/pkg/ginx" - "github.com/toolkits/pkg/logger" -) - -func logSampleFilterAdd(c *gin.Context) { - var f map[string][]string - ginx.BindJSON(c, &f) - - memsto.LogSampleCache.Set(f) - c.JSON(200, "ok") -} - -func logSampleFilterGet(c *gin.Context) { - c.JSON(200, memsto.LogSampleCache.Get()) -} - -func logSampleFilterDel(c *gin.Context) { - memsto.LogSampleCache.Clean() - c.JSON(200, "ok") -} - -func LogSample(remoteAddr string, v *prompb.TimeSeries) { - if memsto.LogSampleCache.Len() == 0 { - return - } - - labelMap := make(map[string]string) - for i := 0; i < len(v.Labels); i++ { - labelMap[v.Labels[i].Name] = v.Labels[i].Value - } - - filterMap := memsto.LogSampleCache.Get() - for k, v := range filterMap { - // 在指标 labels 中找过滤的 label key ,如果找不到,直接返回 - lableValue, exists := labelMap[k] - if !exists { - return - } - - // key 存在,在过滤条件中找指标的 label value,如果找不到,直接返回 - _, exists = v[lableValue] - if !exists { - return - } - } - - // 每个过滤条件都在 指标的 labels 中找到了 - logger.Debugf("recv sample from:%s sample:%s", remoteAddr, v.String()) -} diff --git a/src/server/router/router_memsto.go b/src/server/router/router_memsto.go deleted file mode 100644 index 65ab2904ca9773c49cc6246e6cdd28d917369ebb..0000000000000000000000000000000000000000 --- a/src/server/router/router_memsto.go +++ /dev/null @@ -1,79 +0,0 @@ -package router - -import ( - "net/http" - "strconv" - "strings" - - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/idents" - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/didi/nightingale/v5/src/server/naming" -) - -func alertRuleGet(c *gin.Context) { - id := ginx.QueryInt64(c, "id") - rule := memsto.AlertRuleCache.Get(id) - c.JSON(200, gin.H{"id": id, "rule": rule}) -} - -func identsGets(c *gin.Context) { - c.JSON(200, idents.Idents.Items()) -} - -func mutesGets(c *gin.Context) { - c.JSON(200, memsto.AlertMuteCache.GetAllStructs()) -} - -func subscribesGets(c *gin.Context) { - c.JSON(200, memsto.AlertSubscribeCache.GetStructs(ginx.QueryInt64(c, "id"))) -} - -func targetGet(c *gin.Context) { - ident := ginx.QueryStr(c, "ident") - target, _ := memsto.TargetCache.Get(ident) - c.JSON(200, gin.H{"ident": ident, "target": target}) -} - -func userGet(c *gin.Context) { - id := ginx.QueryInt64(c, "id") - user := memsto.UserCache.GetByUserId(id) - c.JSON(200, gin.H{"id": id, "user": user}) -} - -func userGroupGet(c *gin.Context) { - id := ginx.QueryInt64(c, "id") - ug := memsto.UserGroupCache.GetByUserGroupId(id) - c.JSON(200, gin.H{"id": id, "user_group": ug}) -} - -func alertRuleLocationGet(c *gin.Context) { - id := ginx.QueryInt64(c, "id") - rule := memsto.AlertRuleCache.Get(id) - if rule == nil { - http.Error(c.Writer, "rule not found", http.StatusNotFound) - return - } - var clusters []string - if rule.Cluster == models.ClusterAll { - clusters = config.ReaderClients.GetClusterNames() - } else { - clusters = strings.Fields(rule.Cluster) - } - - var arr []gin.H - for _, cluster := range clusters { - node, err := naming.ClusterHashRing.GetNode(cluster, strconv.FormatInt(id, 10)) - if err != nil { - http.Error(c.Writer, err.Error(), http.StatusInternalServerError) - return - } - arr = append(arr, gin.H{"id": id, "cluster": cluster, "node": node}) - } - - c.JSON(200, gin.H{"list": arr}) -} diff --git a/src/server/server.go b/src/server/server.go deleted file mode 100644 index 19236f570a3e4dbdb812fa1ca9356adf3fe3d578..0000000000000000000000000000000000000000 --- a/src/server/server.go +++ /dev/null @@ -1,189 +0,0 @@ -package server - -import ( - "context" - "fmt" - "os" - "os/signal" - "path/filepath" - "syscall" - - "github.com/toolkits/pkg/i18n" - "github.com/toolkits/pkg/logger" - - "github.com/didi/nightingale/v5/src/pkg/httpx" - "github.com/didi/nightingale/v5/src/pkg/logx" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/didi/nightingale/v5/src/server/engine" - "github.com/didi/nightingale/v5/src/server/idents" - "github.com/didi/nightingale/v5/src/server/memsto" - "github.com/didi/nightingale/v5/src/server/naming" - "github.com/didi/nightingale/v5/src/server/router" - "github.com/didi/nightingale/v5/src/server/stat" - "github.com/didi/nightingale/v5/src/server/usage" - "github.com/didi/nightingale/v5/src/server/writer" - "github.com/didi/nightingale/v5/src/storage" -) - -type Server struct { - ConfigFile string - Version string - Key string -} - -type ServerOption func(*Server) - -func SetConfigFile(f string) ServerOption { - return func(s *Server) { - s.ConfigFile = f - } -} - -func SetVersion(v string) ServerOption { - return func(s *Server) { - s.Version = v - } -} - -func SetKey(k string) ServerOption { - return func(s *Server) { - s.Key = k - } -} - -// Run run server -func Run(opts ...ServerOption) { - code := 1 - sc := make(chan os.Signal, 1) - signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - - server := Server{ - ConfigFile: filepath.Join("etc", "server.conf"), - Version: "not specified", - } - - for _, opt := range opts { - opt(&server) - } - - cleanFunc, err := server.initialize() - if err != nil { - fmt.Println("server init fail:", err) - os.Exit(code) - } - -EXIT: - for { - sig := <-sc - fmt.Println("received signal:", sig.String()) - switch sig { - case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: - code = 0 - break EXIT - case syscall.SIGHUP: - // reload configuration? - reload() - default: - break EXIT - } - } - - cleanFunc() - fmt.Println("server exited") - os.Exit(code) -} - -func (s Server) initialize() (func(), error) { - fns := Functions{} - ctx, cancel := context.WithCancel(context.Background()) - fns.Add(cancel) - - // parse config file - config.MustLoad(s.Key, s.ConfigFile) - - // init i18n - i18n.Init() - - // init logger - loggerClean, err := logx.Init(config.C.Log) - if err != nil { - return fns.Ret(), err - } else { - fns.Add(loggerClean) - } - - // init database - if err = storage.InitDB(config.C.DB); err != nil { - return fns.Ret(), err - } - - // init redis - redisClean, err := storage.InitRedis(config.C.Redis) - if err != nil { - return fns.Ret(), err - } else { - fns.Add(redisClean) - } - - // init prometheus remote writers - if err = writer.Init(config.C.Writers, config.C.WriterOpt); err != nil { - return fns.Ret(), err - } - - // init prometheus remote reader - if err = config.InitReader(); err != nil { - return fns.Ret(), err - } - - // sync rules/users/mutes/targets to memory cache - memsto.Sync() - - // start heartbeat - if err = naming.Heartbeat(ctx); err != nil { - return fns.Ret(), err - } - - // start judge engine - if err = engine.Start(ctx); err != nil { - return fns.Ret(), err - } - - stat.Init() - - // init http server - r := router.New(s.Version, reload) - httpClean := httpx.Init(config.C.HTTP, r) - fns.Add(httpClean) - - // register ident and nodata logic - idents.Handle(ctx) - - if !config.C.DisableUsageReport { - go usage.Report() - } - - // release all the resources - return fns.Ret(), nil -} - -type Functions struct { - List []func() -} - -func (fs *Functions) Add(f func()) { - fs.List = append(fs.List, f) -} - -func (fs *Functions) Ret() func() { - return func() { - for i := 0; i < len(fs.List); i++ { - fs.List[i]() - } - } -} - -func reload() { - logger.Info("start reload configs") - engine.Reload() - logger.Info("reload configs finished") -} diff --git a/src/server/usage/usage.go b/src/server/usage/usage.go deleted file mode 100644 index 3fd0e6ac8589509fed55542ceecec9dee5b51c75..0000000000000000000000000000000000000000 --- a/src/server/usage/usage.go +++ /dev/null @@ -1,91 +0,0 @@ -package usage - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "os" - "time" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/version" -) - -const ( - url = "http://n9e.io/report" - request = "sum(rate(n9e_server_samples_received_total[5m]))" -) - -type Usage struct { - Samples float64 `json:"samples"` // per second - Users float64 `json:"users"` // user total - Maintainer string `json:"maintainer"` - Hostname string `json:"hostname"` - Version string `json:"version"` -} - -func Report() { - for { - time.Sleep(time.Minute * 10) - report() - } -} - -func report() { - tnum, err := models.TargetTotalCount() - if err != nil { - return - } - - hostname, err := os.Hostname() - if err != nil { - return - } - - unum, err := models.UserTotal("") - if err != nil { - return - } - - maintainer := "blank" - - u := Usage{ - Samples: float64(tnum), - Users: float64(unum), - Hostname: hostname, - Maintainer: maintainer, - Version: version.VERSION, - } - - post(u) -} - -func post(u Usage) error { - body, err := json.Marshal(u) - if err != nil { - return err - } - - req, err := http.NewRequest("POST", url, bytes.NewReader(body)) - if err != nil { - return err - } - - cli := http.Client{ - Timeout: time.Second * 10, - } - - resp, err := cli.Do(req) - if err != nil { - return err - } - - if resp.StatusCode != 200 { - return fmt.Errorf("got %s", resp.Status) - } - - _, err = ioutil.ReadAll(resp.Body) - return err -} diff --git a/src/server/writer/writer.go b/src/server/writer/writer.go deleted file mode 100644 index 81b4a14e864cc21375f0e5d85e5ec19a469607a7..0000000000000000000000000000000000000000 --- a/src/server/writer/writer.go +++ /dev/null @@ -1,252 +0,0 @@ -package writer - -import ( - "bytes" - "context" - "fmt" - "hash/crc32" - "net" - "net/http" - "time" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/server/config" - "github.com/golang/protobuf/proto" - "github.com/golang/snappy" - "github.com/prometheus/client_golang/api" - "github.com/prometheus/prometheus/prompb" - "github.com/toolkits/pkg/logger" - - promstat "github.com/didi/nightingale/v5/src/server/stat" -) - -type WriterType struct { - Opts config.WriterOptions - Client api.Client -} - -func (w WriterType) writeRelabel(items []*prompb.TimeSeries) []*prompb.TimeSeries { - ritems := make([]*prompb.TimeSeries, 0, len(items)) - for _, item := range items { - lbls := models.Process(item.Labels, w.Opts.WriteRelabels...) - if len(lbls) == 0 { - continue - } - ritems = append(ritems, item) - } - return ritems -} - -func (w WriterType) Write(cluster string, index int, items []*prompb.TimeSeries, headers ...map[string]string) { - if len(items) == 0 { - return - } - - items = w.writeRelabel(items) - if len(items) == 0 { - return - } - - start := time.Now() - defer func() { - if cluster != "" { - promstat.ForwardDuration.WithLabelValues(cluster, fmt.Sprint(index)).Observe(time.Since(start).Seconds()) - } - }() - - if config.C.ForceUseServerTS { - ts := start.UnixMilli() - for i := 0; i < len(items); i++ { - if len(items[i].Samples) == 0 { - continue - } - items[i].Samples[0].Timestamp = ts - } - } - - req := &prompb.WriteRequest{ - Timeseries: items, - } - - data, err := proto.Marshal(req) - if err != nil { - logger.Warningf("marshal prom data to proto got error: %v, data: %+v", err, items) - return - } - - if err := w.Post(snappy.Encode(nil, data), headers...); err != nil { - logger.Warningf("post to %s got error: %v", w.Opts.Url, err) - logger.Warning("example timeseries:", items[0].String()) - } -} - -func (w WriterType) Post(req []byte, headers ...map[string]string) error { - httpReq, err := http.NewRequest("POST", w.Opts.Url, bytes.NewReader(req)) - if err != nil { - logger.Warningf("create remote write request got error: %s", err.Error()) - return err - } - - httpReq.Header.Add("Content-Encoding", "snappy") - httpReq.Header.Set("Content-Type", "application/x-protobuf") - httpReq.Header.Set("User-Agent", "n9e") - httpReq.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0") - - if len(headers) > 0 { - for k, v := range headers[0] { - httpReq.Header.Set(k, v) - } - } - - if w.Opts.BasicAuthUser != "" { - httpReq.SetBasicAuth(w.Opts.BasicAuthUser, w.Opts.BasicAuthPass) - } - - headerCount := len(w.Opts.Headers) - if headerCount > 0 && headerCount%2 == 0 { - for i := 0; i < len(w.Opts.Headers); i += 2 { - httpReq.Header.Add(w.Opts.Headers[i], w.Opts.Headers[i+1]) - if w.Opts.Headers[i] == "Host" { - httpReq.Host = w.Opts.Headers[i+1] - } - } - } - - resp, body, err := w.Client.Do(context.Background(), httpReq) - if err != nil { - logger.Warningf("push data with remote write request got error: %v, response body: %s", err, string(body)) - return err - } - - if resp.StatusCode >= 400 { - err = fmt.Errorf("push data with remote write request got status code: %v, response body: %s", resp.StatusCode, string(body)) - return err - } - - return nil -} - -type WritersType struct { - globalOpt config.WriterGlobalOpt - backends map[string]WriterType - queues map[string]map[int]*SafeListLimited -} - -func (ws *WritersType) Put(name string, writer WriterType) { - ws.backends[name] = writer -} - -func (ws *WritersType) PushSample(ident string, v interface{}, clusters ...string) { - hashkey := crc32.ChecksumIEEE([]byte(ident)) % uint32(ws.globalOpt.QueueCount) - - cluster := config.C.ClusterName - if len(clusters) > 0 { - cluster = clusters[0] - } - - if _, ok := ws.queues[cluster]; !ok { - // 待写入的集群不存在 - logger.Warningf("Write cluster:%s not found, v:%+v", cluster, v) - return - } - - c, ok := ws.queues[cluster][int(hashkey)] - if ok { - succ := c.PushFront(v) - if !succ { - logger.Warningf("Write cluster:%s channel(%s) full, current channel size: %d", cluster, ident, c.Len()) - } - } -} - -func (ws *WritersType) StartConsumer(index int, ch *SafeListLimited, clusterName string) { - for { - series := ch.PopBack(ws.globalOpt.QueuePopSize) - if len(series) == 0 { - time.Sleep(time.Millisecond * 400) - continue - } - - for key := range ws.backends { - if ws.backends[key].Opts.ClusterName != clusterName { - continue - } - go ws.backends[key].Write(clusterName, index, series) - } - } -} - -func NewWriters() WritersType { - return WritersType{ - backends: make(map[string]WriterType), - } -} - -var Writers = NewWriters() - -func Init(opts []config.WriterOptions, globalOpt config.WriterGlobalOpt) error { - Writers.globalOpt = globalOpt - Writers.queues = make(map[string]map[int]*SafeListLimited) - for _, opt := range opts { - if _, ok := Writers.queues[opt.ClusterName]; !ok { - Writers.queues[opt.ClusterName] = make(map[int]*SafeListLimited) - for i := 0; i < globalOpt.QueueCount; i++ { - Writers.queues[opt.ClusterName][i] = NewSafeListLimited(Writers.globalOpt.QueueMaxSize) - go Writers.StartConsumer(i, Writers.queues[opt.ClusterName][i], opt.ClusterName) - } - } - } - - go reportChanSize() - - for i := 0; i < len(opts); i++ { - cli, err := api.NewClient(api.Config{ - Address: opts[i].Url, - RoundTripper: &http.Transport{ - // TLSClientConfig: tlsConfig, - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: time.Duration(opts[i].DialTimeout) * time.Millisecond, - KeepAlive: time.Duration(opts[i].KeepAlive) * time.Millisecond, - }).DialContext, - ResponseHeaderTimeout: time.Duration(opts[i].Timeout) * time.Millisecond, - TLSHandshakeTimeout: time.Duration(opts[i].TLSHandshakeTimeout) * time.Millisecond, - ExpectContinueTimeout: time.Duration(opts[i].ExpectContinueTimeout) * time.Millisecond, - MaxConnsPerHost: opts[i].MaxConnsPerHost, - MaxIdleConns: opts[i].MaxIdleConns, - MaxIdleConnsPerHost: opts[i].MaxIdleConnsPerHost, - IdleConnTimeout: time.Duration(opts[i].IdleConnTimeout) * time.Millisecond, - }, - }) - - if err != nil { - return err - } - - writer := WriterType{ - Opts: opts[i], - Client: cli, - } - - Writers.Put(opts[i].Url, writer) - } - - return nil -} - -func reportChanSize() { - clusterName := config.C.ClusterName - if clusterName == "" { - return - } - - for { - time.Sleep(time.Second * 3) - for cluster, m := range Writers.queues { - for i, c := range m { - size := c.Len() - promstat.GaugeSampleQueueSize.WithLabelValues(cluster, fmt.Sprint(i)).Set(float64(size)) - } - } - } -} diff --git a/src/webapi/config/config.go b/src/webapi/config/config.go deleted file mode 100644 index 4740c174fe3a8c6f6c77d75e36ea54a909ecb37e..0000000000000000000000000000000000000000 --- a/src/webapi/config/config.go +++ /dev/null @@ -1,204 +0,0 @@ -package config - -import ( - "fmt" - "os" - "strings" - "sync" - - "github.com/gin-gonic/gin" - "github.com/koding/multiconfig" - - "github.com/didi/nightingale/v5/src/pkg/cas" - "github.com/didi/nightingale/v5/src/pkg/httpx" - "github.com/didi/nightingale/v5/src/pkg/ldapx" - "github.com/didi/nightingale/v5/src/pkg/logx" - "github.com/didi/nightingale/v5/src/pkg/oauth2x" - "github.com/didi/nightingale/v5/src/pkg/oidcc" - "github.com/didi/nightingale/v5/src/pkg/ormx" - "github.com/didi/nightingale/v5/src/pkg/secu" - "github.com/didi/nightingale/v5/src/pkg/tls" - "github.com/didi/nightingale/v5/src/storage" -) - -var ( - C = new(Config) - once sync.Once -) - -func DealConfigCrypto(key string) { - decryptDsn, err := secu.DealWithDecrypt(C.DB.DSN, key) - if err != nil { - fmt.Println("failed to decrypt the db dsn", err) - os.Exit(1) - } - C.DB.DSN = decryptDsn - - decryptRedisPwd, err := secu.DealWithDecrypt(C.Redis.Password, key) - if err != nil { - fmt.Println("failed to decrypt the redis password", err) - os.Exit(1) - } - C.Redis.Password = decryptRedisPwd - - decryptIbexPwd, err := secu.DealWithDecrypt(C.Ibex.BasicAuthPass, key) - if err != nil { - fmt.Println("failed to decrypt the ibex password", err) - os.Exit(1) - } - C.Ibex.BasicAuthPass = decryptIbexPwd - - for index, v := range C.Clusters { - decryptClusterPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, key) - if err != nil { - fmt.Printf("failed to decrypt the clusters password: %s , error: %s", v.BasicAuthPass, err.Error()) - os.Exit(1) - } - C.Clusters[index].BasicAuthPass = decryptClusterPwd - } - -} - -func MustLoad(key string, fpaths ...string) { - once.Do(func() { - loaders := []multiconfig.Loader{ - &multiconfig.TagLoader{}, - &multiconfig.EnvironmentLoader{}, - } - - for _, fpath := range fpaths { - handled := false - - if strings.HasSuffix(fpath, "toml") { - loaders = append(loaders, &multiconfig.TOMLLoader{Path: fpath}) - handled = true - } - if strings.HasSuffix(fpath, "conf") { - loaders = append(loaders, &multiconfig.TOMLLoader{Path: fpath}) - handled = true - } - if strings.HasSuffix(fpath, "json") { - loaders = append(loaders, &multiconfig.JSONLoader{Path: fpath}) - handled = true - } - if strings.HasSuffix(fpath, "yaml") { - loaders = append(loaders, &multiconfig.YAMLLoader{Path: fpath}) - handled = true - } - - if !handled { - fmt.Println("config file invalid, valid file exts: .conf,.yaml,.toml,.json") - os.Exit(1) - } - } - - m := multiconfig.DefaultLoader{ - Loader: multiconfig.MultiLoader(loaders...), - Validator: multiconfig.MultiValidator(&multiconfig.RequiredValidator{}), - } - - m.MustLoad(C) - - DealConfigCrypto(key) - - if !strings.HasPrefix(C.Ibex.Address, "http") { - C.Ibex.Address = "http://" + C.Ibex.Address - } - - err := loadMetricsYaml() - if err != nil { - fmt.Println("failed to load metrics.yaml:", err) - os.Exit(1) - } - }) -} - -type Config struct { - RunMode string - I18N string - I18NHeaderKey string - AdminRole string - MetricsYamlFile string - BuiltinAlertsDir string - BuiltinDashboardsDir string - ClustersFrom string - ClustersFromAPIs []string - ContactKeys []LabelAndKey - NotifyChannels []LabelAndKey - Log logx.Config - HTTP httpx.Config - JWTAuth JWTAuth - ProxyAuth ProxyAuth - BasicAuth gin.Accounts - AnonymousAccess AnonymousAccess - LDAP ldapx.LdapSection - Redis storage.RedisConfig - DB ormx.DBConfig - Clusters []ClusterOptions - Ibex Ibex - OIDC oidcc.Config - CAS cas.Config - OAuth oauth2x.Config - TargetMetrics map[string]string -} - -type ClusterOptions struct { - Name string - Prom string - - BasicAuthUser string - BasicAuthPass string - - Headers []string - - Timeout int64 - DialTimeout int64 - - UseTLS bool - tls.ClientConfig - - MaxIdleConnsPerHost int -} - -type LabelAndKey struct { - Label string `json:"label"` - Key string `json:"key"` -} - -func LabelAndKeyHasKey(keys []LabelAndKey, key string) bool { - for i := 0; i < len(keys); i++ { - if keys[i].Key == key { - return true - } - } - return false -} - -type JWTAuth struct { - SigningKey string - AccessExpired int64 - RefreshExpired int64 - RedisKeyPrefix string -} - -type ProxyAuth struct { - Enable bool - HeaderUserNameKey string - DefaultRoles []string -} - -type AnonymousAccess struct { - PromQuerier bool - AlertDetail bool -} - -type Ibex struct { - Address string - BasicAuthUser string - BasicAuthPass string - Timeout int64 -} - -func (c *Config) IsDebugMode() bool { - return c.RunMode == "debug" -} diff --git a/src/webapi/config/i18n.go b/src/webapi/config/i18n.go deleted file mode 100644 index 9431f5dbfcc4a7d193facdba872108d646fa7533..0000000000000000000000000000000000000000 --- a/src/webapi/config/i18n.go +++ /dev/null @@ -1,51 +0,0 @@ -package config - -var ( - dict = map[string]string{ - "just a test": "这只是一个测试", - "just a test: %s": "这只是一个测试: %s", - "Internal Server Error": "系统内部错误,请联系管理员", - "Username or password invalid": "登录失败,请检查用户名和密码", - "Username is blank": "用户名不能为空", - "Username has invalid characters": "用户名含有非法字符", - "Nickname has invalid characters": "昵称含有非法字符", - "Phone invalid": "手机号格式非法", - "Email invalid": "邮箱格式非法", - "Incorrect old password": "旧密码错误", - "Username already exists": "用户名已存在", - "No such user": "用户不存在", - "Note has invalid characters": "备注含有非法字符", - "UserGroup already exists": "用户组已存在,不能重复创建", - "No such UserGroup": "用户组不存在", - "No such BusiGroup": "业务组不存在", - "BusiGroup already exists": "业务分组已存在,不能重复创建", - "Some UserGroup id not exists": "有些用户组ID不存在", - "Some alert mutes still in the BusiGroup": "业务组下仍然存在告警屏蔽配置,不能删除", - "Some dashboards still in the BusiGroup": "业务组下仍然存在监控大盘配置,不能删除", - "Some collect rules still in the BusiGroup": "业务组下仍然存在采集规则配置,不能删除", - "Some alert rules still in the BusiGroup": "业务组下仍然存在告警规则配置,不能删除", - "Some alert subscribes still in the BusiGroup": "业务组下仍然存在订阅规则配置,不能删除", - "Some targets still in the BusiGroup": "业务组下仍然存在监控对象,不能删除", - "Some recovery scripts still in the BusiGroup": "业务组下仍然存在自愈脚本,不能删除", - "Name is blank": "名称不能为空", - "Name has invalid characters": "名称含有非法字符", - "Dashboard already exists": "监控大盘已存在", - "Ident duplicate": "英文标识已存在", - "No such dashboard": "监控大盘不存在", - "AlertRule already exists": "告警规则已存在,不能重复创建", - "No such AlertRule": "告警规则不存在", - "CollectRule already exists": "采集规则已存在,不能重复创建", - "No such metric description": "该指标释义不存在,可能已被删除", - "No such TargetQuery": "查询条件不存在,可能已被删除", - "No permission. Only admins can assign BG": "没有权限!只有管理员才能分配业务组", - "No permission to operate the targets: %s": "没有权限操作这些监控对象:%s", - "No permission. You are not admin of BG(%s)": "没有权限操作,您并非业务组(%s)的管理员", - "The business group must retain at least one team": "业务组下要保留至少一个团队", - "At least one team have rw permission": "业务组下至少要有一个具备读写权限的团队", - "duplicate tagkey(%s)": "标签KEY(%s)重复了", - "Failed to create BusiGroup(%s)": "创建业务(%s)组失败", - } - langDict = map[string]map[string]string{ - "zh": dict, - } -) diff --git a/src/webapi/config/init.go b/src/webapi/config/init.go deleted file mode 100644 index 74793cf9441b1f928ce92a854557c752e646ddb5..0000000000000000000000000000000000000000 --- a/src/webapi/config/init.go +++ /dev/null @@ -1,7 +0,0 @@ -package config - -import "github.com/toolkits/pkg/i18n" - -func init() { - i18n.DictRegister(langDict) -} diff --git a/src/webapi/prom/prom.go b/src/webapi/prom/prom.go deleted file mode 100644 index d67574fe7c741b1a7bd32374645f2c94cae21580..0000000000000000000000000000000000000000 --- a/src/webapi/prom/prom.go +++ /dev/null @@ -1,292 +0,0 @@ -package prom - -import ( - "encoding/json" - "fmt" - "io" - "math/rand" - "net" - "net/http" - "sort" - "strings" - "sync" - "time" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/prom" - "github.com/didi/nightingale/v5/src/webapi/config" - "github.com/prometheus/client_golang/api" - "github.com/toolkits/pkg/logger" - "github.com/toolkits/pkg/net/httplib" -) - -type ClusterType struct { - Opts config.ClusterOptions - Transport *http.Transport - PromClient prom.API -} - -type ClustersType struct { - datas map[string]*ClusterType - mutex *sync.RWMutex -} - -type PromOption struct { - Url string - User string - Pass string - Headers []string - Timeout int64 - DialTimeout int64 - MaxIdleConnsPerHost int -} - -func (cs *ClustersType) Put(name string, cluster *ClusterType) { - cs.mutex.Lock() - defer cs.mutex.Unlock() - - cs.datas[name] = cluster - - // 把配置信息写入DB一份,这样n9e-server就可以直接从DB读取了 - po := PromOption{ - Url: cluster.Opts.Prom, - User: cluster.Opts.BasicAuthUser, - Pass: cluster.Opts.BasicAuthPass, - Headers: cluster.Opts.Headers, - Timeout: cluster.Opts.Timeout, - DialTimeout: cluster.Opts.DialTimeout, - MaxIdleConnsPerHost: cluster.Opts.MaxIdleConnsPerHost, - } - - bs, err := json.Marshal(po) - if err != nil { - logger.Fatal("failed to marshal PromOption:", err) - return - } - - key := "prom." + name + ".option" - err = models.ConfigsSet(key, string(bs)) - if err != nil { - logger.Fatal("failed to set PromOption ", key, " to database, error: ", err) - } -} - -func (cs *ClustersType) Get(name string) (*ClusterType, bool) { - cs.mutex.RLock() - c, has := cs.datas[name] - cs.mutex.RUnlock() - return c, has -} - -var Clusters = ClustersType{ - datas: make(map[string]*ClusterType), - mutex: new(sync.RWMutex), -} - -func Init() error { - cf := strings.ToLower(strings.TrimSpace(config.C.ClustersFrom)) - if cf == "" || cf == "config" { - return initClustersFromConfig() - } - - if cf == "api" { - return initClustersFromAPI() - } - - return fmt.Errorf("invalid configuration ClustersFrom: %s", cf) -} - -func initClustersFromConfig() error { - opts := config.C.Clusters - - for i := 0; i < len(opts); i++ { - cluster := newClusterByOption(opts[i]) - if cluster == nil { - continue - } - Clusters.Put(opts[i].Name, cluster) - } - - return nil -} - -type DSReply struct { - RequestID string `json:"request_id"` - Data struct { - Items []struct { - Name string `json:"name"` - Settings struct { - PrometheusAddr string `json:"prometheus.addr"` - PrometheusBasic struct { - PrometheusUser string `json:"prometheus.user"` - PrometheusPass string `json:"prometheus.password"` - } `json:"prometheus.basic"` - Headers map[string]string `json:"prometheus.headers"` - PrometheusTimeout int64 `json:"prometheus.timeout"` - } `json:"settings,omitempty"` - } `json:"items"` - } `json:"data"` -} - -func initClustersFromAPI() error { - go func() { - for { - loadClustersFromAPI() - time.Sleep(time.Second * 3) - } - }() - return nil -} - -func loadClustersFromAPI() { - urls := config.C.ClustersFromAPIs - if len(urls) == 0 { - logger.Error("configuration(ClustersFromAPIs) empty") - return - } - - var reply DSReply - - count := len(urls) - for _, i := range rand.Perm(count) { - url := urls[i] - - res, err := httplib.Post(url).SetTimeout(time.Duration(3000) * time.Millisecond).Response() - if err != nil { - logger.Errorf("curl %s fail: %v", url, err) - continue - } - - if res.StatusCode != 200 { - logger.Errorf("curl %s fail, status code: %d", url, res.StatusCode) - continue - } - - defer res.Body.Close() - - jsonBytes, err := io.ReadAll(res.Body) - if err != nil { - logger.Errorf("read response body of %s fail: %v", url, err) - continue - } - logger.Debugf("curl %s success, response: %s", url, string(jsonBytes)) - - err = json.Unmarshal(jsonBytes, &reply) - if err != nil { - logger.Errorf("unmarshal response body of %s fail: %v", url, err) - continue - } - - break - } - - for _, item := range reply.Data.Items { - if item.Settings.PrometheusAddr == "" { - continue - } - - if item.Name == "" { - continue - } - - old, has := Clusters.Get(item.Name) - if !has || - old.Opts.BasicAuthUser != item.Settings.PrometheusBasic.PrometheusUser || - old.Opts.BasicAuthPass != item.Settings.PrometheusBasic.PrometheusPass || - old.Opts.Timeout != item.Settings.PrometheusTimeout || - old.Opts.Prom != item.Settings.PrometheusAddr || - !equalHeader(old.Opts.Headers, transformHeader(item.Settings.Headers)) { - opt := config.ClusterOptions{ - Name: item.Name, - Prom: item.Settings.PrometheusAddr, - BasicAuthUser: item.Settings.PrometheusBasic.PrometheusUser, - BasicAuthPass: item.Settings.PrometheusBasic.PrometheusPass, - Timeout: item.Settings.PrometheusTimeout, - DialTimeout: 5000, - MaxIdleConnsPerHost: 32, - Headers: transformHeader(item.Settings.Headers), - } - - if strings.HasPrefix(opt.Prom, "https") { - opt.UseTLS = true - opt.InsecureSkipVerify = true - } - - cluster := newClusterByOption(opt) - if cluster == nil { - continue - } - - Clusters.Put(item.Name, cluster) - continue - } - } -} - -func newClusterByOption(opt config.ClusterOptions) *ClusterType { - transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: time.Duration(opt.DialTimeout) * time.Millisecond, - }).DialContext, - ResponseHeaderTimeout: time.Duration(opt.Timeout) * time.Millisecond, - MaxIdleConnsPerHost: opt.MaxIdleConnsPerHost, - } - - if opt.UseTLS { - tlsConfig, err := opt.TLSConfig() - if err != nil { - logger.Errorf("new cluster %s fail: %v", opt.Name, err) - return nil - } - transport.TLSClientConfig = tlsConfig - } - - cli, err := api.NewClient(api.Config{ - Address: opt.Prom, - RoundTripper: transport, - }) - - if err != nil { - logger.Errorf("new client fail: %v", err) - return nil - } - - cluster := &ClusterType{ - Opts: opt, - Transport: transport, - PromClient: prom.NewAPI(cli, prom.ClientOptions{ - BasicAuthUser: opt.BasicAuthUser, - BasicAuthPass: opt.BasicAuthPass, - Headers: opt.Headers, - }), - } - - return cluster -} - -func equalHeader(a, b []string) bool { - sort.Strings(a) - sort.Strings(b) - - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - -func transformHeader(header map[string]string) []string { - var headers []string - for k, v := range header { - headers = append(headers, k) - headers = append(headers, v) - } - return headers -} diff --git a/src/webapi/router/router.go b/src/webapi/router/router.go deleted file mode 100644 index 29b7913ec6dc7db3f53fbff4fb2d38e5540fe53c..0000000000000000000000000000000000000000 --- a/src/webapi/router/router.go +++ /dev/null @@ -1,346 +0,0 @@ -package router - -import ( - "fmt" - "os" - "path" - "strings" - "time" - - "github.com/gin-contrib/pprof" - "github.com/gin-gonic/gin" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/pkg/aop" - "github.com/didi/nightingale/v5/src/webapi/config" - promstat "github.com/didi/nightingale/v5/src/webapi/stat" -) - -func stat() gin.HandlerFunc { - return func(c *gin.Context) { - start := time.Now() - c.Next() - - code := fmt.Sprintf("%d", c.Writer.Status()) - method := c.Request.Method - labels := []string{promstat.Service, code, c.FullPath(), method} - - promstat.RequestCounter.WithLabelValues(labels...).Inc() - promstat.RequestDuration.WithLabelValues(labels...).Observe(float64(time.Since(start).Seconds())) - } -} - -func languageDetector() gin.HandlerFunc { - headerKey := config.C.I18NHeaderKey - return func(c *gin.Context) { - if headerKey != "" { - lang := c.GetHeader(headerKey) - if lang != "" { - if strings.HasPrefix(lang, "*") || strings.HasPrefix(lang, "zh") { - c.Request.Header.Set("X-Language", "zh") - } else if strings.HasPrefix(lang, "en") { - c.Request.Header.Set("X-Language", "en") - } else { - c.Request.Header.Set("X-Language", lang) - } - } - } - c.Next() - } -} - -func New(version string) *gin.Engine { - gin.SetMode(config.C.RunMode) - - if strings.ToLower(config.C.RunMode) == "release" { - aop.DisableConsoleColor() - } - - r := gin.New() - - r.Use(stat()) - r.Use(languageDetector()) - r.Use(aop.Recovery()) - - // whether print access log - if config.C.HTTP.PrintAccessLog { - r.Use(aop.Logger()) - } - - configRoute(r, version) - configNoRoute(r) - - return r -} - -func configNoRoute(r *gin.Engine) { - r.NoRoute(func(c *gin.Context) { - arr := strings.Split(c.Request.URL.Path, ".") - suffix := arr[len(arr)-1] - switch suffix { - case "png", "jpeg", "jpg", "svg", "ico", "gif", "css", "js", "html", "htm", "gz", "zip", "map": - c.File(path.Join(strings.Split("pub/"+c.Request.URL.Path, "/")...)) - default: - c.File(path.Join("pub", "index.html")) - } - }) -} - -func configRoute(r *gin.Engine, version string) { - if config.C.HTTP.PProf { - pprof.Register(r, "/api/debug/pprof") - } - - r.GET("/ping", func(c *gin.Context) { - c.String(200, "pong") - }) - - r.GET("/pid", func(c *gin.Context) { - c.String(200, fmt.Sprintf("%d", os.Getpid())) - }) - - r.GET("/addr", func(c *gin.Context) { - c.String(200, c.Request.RemoteAddr) - }) - - r.GET("/version", func(c *gin.Context) { - c.String(200, version) - }) - - r.GET("/i18n", func(c *gin.Context) { - ginx.NewRender(c).Message("just a test: %s", "by ulric") - }) - - r.GET("/metrics", gin.WrapH(promhttp.Handler())) - - pagesPrefix := "/api/n9e" - - pages := r.Group(pagesPrefix) - { - if config.C.AnonymousAccess.PromQuerier { - pages.Any("/prometheus/*url", prometheusProxy) - pages.POST("/query-range-batch", promBatchQueryRange) - } else { - pages.Any("/prometheus/*url", auth(), prometheusProxy) - pages.POST("/query-range-batch", auth(), promBatchQueryRange) - } - - pages.GET("/version", func(c *gin.Context) { - c.String(200, version) - }) - - pages.POST("/auth/login", jwtMock(), loginPost) - pages.POST("/auth/logout", jwtMock(), logoutPost) - pages.POST("/auth/refresh", jwtMock(), refreshPost) - - pages.GET("/auth/sso-config", ssoConfigGet) - pages.GET("/auth/redirect", loginRedirect) - pages.GET("/auth/redirect/cas", loginRedirectCas) - pages.GET("/auth/redirect/oauth", loginRedirectOAuth) - pages.GET("/auth/callback", loginCallback) - pages.GET("/auth/callback/cas", loginCallbackCas) - pages.GET("/auth/callback/oauth", loginCallbackOAuth) - - pages.GET("/metrics/desc", metricsDescGetFile) - pages.POST("/metrics/desc", metricsDescGetMap) - - pages.GET("/roles", rolesGets) - pages.GET("/notify-channels", notifyChannelsGets) - pages.GET("/contact-keys", contactKeysGets) - pages.GET("/clusters", clustersGets) - - pages.GET("/self/perms", auth(), user(), permsGets) - pages.GET("/self/profile", auth(), user(), selfProfileGet) - pages.PUT("/self/profile", auth(), user(), selfProfilePut) - pages.PUT("/self/password", auth(), user(), selfPasswordPut) - - pages.GET("/users", auth(), user(), perm("/users"), userGets) - pages.POST("/users", auth(), admin(), userAddPost) - pages.GET("/user/:id/profile", auth(), userProfileGet) - pages.PUT("/user/:id/profile", auth(), admin(), userProfilePut) - pages.PUT("/user/:id/password", auth(), admin(), userPasswordPut) - pages.DELETE("/user/:id", auth(), admin(), userDel) - - pages.GET("/metric-views", auth(), metricViewGets) - pages.DELETE("/metric-views", auth(), user(), metricViewDel) - pages.POST("/metric-views", auth(), user(), metricViewAdd) - pages.PUT("/metric-views", auth(), user(), metricViewPut) - - pages.GET("/user-groups", auth(), user(), userGroupGets) - pages.POST("/user-groups", auth(), user(), perm("/user-groups/add"), userGroupAdd) - pages.GET("/user-group/:id", auth(), user(), userGroupGet) - pages.PUT("/user-group/:id", auth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupPut) - pages.DELETE("/user-group/:id", auth(), user(), perm("/user-groups/del"), userGroupWrite(), userGroupDel) - pages.POST("/user-group/:id/members", auth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupMemberAdd) - pages.DELETE("/user-group/:id/members", auth(), user(), perm("/user-groups/put"), userGroupWrite(), userGroupMemberDel) - - pages.GET("/busi-groups", auth(), user(), busiGroupGets) - pages.POST("/busi-groups", auth(), user(), perm("/busi-groups/add"), busiGroupAdd) - pages.GET("/busi-groups/alertings", auth(), busiGroupAlertingsGets) - pages.GET("/busi-group/:id", auth(), user(), bgro(), busiGroupGet) - pages.PUT("/busi-group/:id", auth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupPut) - pages.POST("/busi-group/:id/members", auth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupMemberAdd) - pages.DELETE("/busi-group/:id/members", auth(), user(), perm("/busi-groups/put"), bgrw(), busiGroupMemberDel) - pages.DELETE("/busi-group/:id", auth(), user(), perm("/busi-groups/del"), bgrw(), busiGroupDel) - pages.GET("/busi-group/:id/perm/:perm", auth(), user(), checkBusiGroupPerm) - - pages.GET("/targets", auth(), user(), targetGets) - pages.DELETE("/targets", auth(), user(), perm("/targets/del"), targetDel) - pages.GET("/targets/tags", auth(), user(), targetGetTags) - pages.POST("/targets/tags", auth(), user(), perm("/targets/put"), targetBindTagsByFE) - pages.DELETE("/targets/tags", auth(), user(), perm("/targets/put"), targetUnbindTagsByFE) - pages.PUT("/targets/note", auth(), user(), perm("/targets/put"), targetUpdateNote) - pages.PUT("/targets/bgid", auth(), user(), perm("/targets/put"), targetUpdateBgid) - - pages.GET("/builtin-boards", builtinBoardGets) - pages.GET("/builtin-board/:name", builtinBoardGet) - - pages.GET("/busi-group/:id/boards", auth(), user(), perm("/dashboards"), bgro(), boardGets) - pages.POST("/busi-group/:id/boards", auth(), user(), perm("/dashboards/add"), bgrw(), boardAdd) - pages.POST("/busi-group/:id/board/:bid/clone", auth(), user(), perm("/dashboards/add"), bgrw(), boardClone) - - pages.GET("/board/:bid", boardGet) - pages.GET("/board/:bid/pure", boardPureGet) - pages.PUT("/board/:bid", auth(), user(), perm("/dashboards/put"), boardPut) - pages.PUT("/board/:bid/configs", auth(), user(), perm("/dashboards/put"), boardPutConfigs) - pages.PUT("/board/:bid/public", auth(), user(), perm("/dashboards/put"), boardPutPublic) - pages.DELETE("/boards", auth(), user(), perm("/dashboards/del"), boardDel) - - // migrate v5.8.0 - pages.GET("/dashboards", auth(), admin(), migrateDashboards) - pages.GET("/dashboard/:id", auth(), admin(), migrateDashboardGet) - pages.PUT("/dashboard/:id/migrate", auth(), admin(), migrateDashboard) - - // deprecated ↓ - pages.GET("/dashboards/builtin/list", builtinBoardGets) - pages.POST("/busi-group/:id/dashboards/builtin", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardBuiltinImport) - pages.GET("/busi-group/:id/dashboards", auth(), user(), perm("/dashboards"), bgro(), dashboardGets) - pages.POST("/busi-group/:id/dashboards", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardAdd) - pages.POST("/busi-group/:id/dashboards/export", auth(), user(), perm("/dashboards"), bgro(), dashboardExport) - pages.POST("/busi-group/:id/dashboards/import", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardImport) - pages.POST("/busi-group/:id/dashboard/:did/clone", auth(), user(), perm("/dashboards/add"), bgrw(), dashboardClone) - pages.GET("/busi-group/:id/dashboard/:did", auth(), user(), perm("/dashboards"), bgro(), dashboardGet) - pages.PUT("/busi-group/:id/dashboard/:did", auth(), user(), perm("/dashboards/put"), bgrw(), dashboardPut) - pages.DELETE("/busi-group/:id/dashboard/:did", auth(), user(), perm("/dashboards/del"), bgrw(), dashboardDel) - - pages.GET("/busi-group/:id/chart-groups", auth(), user(), bgro(), chartGroupGets) - pages.POST("/busi-group/:id/chart-groups", auth(), user(), bgrw(), chartGroupAdd) - pages.PUT("/busi-group/:id/chart-groups", auth(), user(), bgrw(), chartGroupPut) - pages.DELETE("/busi-group/:id/chart-groups", auth(), user(), bgrw(), chartGroupDel) - - pages.GET("/busi-group/:id/charts", auth(), user(), bgro(), chartGets) - pages.POST("/busi-group/:id/charts", auth(), user(), bgrw(), chartAdd) - pages.PUT("/busi-group/:id/charts", auth(), user(), bgrw(), chartPut) - pages.DELETE("/busi-group/:id/charts", auth(), user(), bgrw(), chartDel) - // deprecated ↑ - - pages.GET("/share-charts", chartShareGets) - pages.POST("/share-charts", auth(), chartShareAdd) - - pages.GET("/alert-rules/builtin/list", alertRuleBuiltinList) - pages.POST("/busi-group/:id/alert-rules/builtin", auth(), user(), perm("/alert-rules/add"), bgrw(), alertRuleBuiltinImport) - pages.GET("/busi-group/:id/alert-rules", auth(), user(), perm("/alert-rules"), alertRuleGets) - pages.POST("/busi-group/:id/alert-rules", auth(), user(), perm("/alert-rules/add"), bgrw(), alertRuleAddByFE) - pages.DELETE("/busi-group/:id/alert-rules", auth(), user(), perm("/alert-rules/del"), bgrw(), alertRuleDel) - pages.PUT("/busi-group/:id/alert-rules/fields", auth(), user(), perm("/alert-rules/put"), bgrw(), alertRulePutFields) - pages.PUT("/busi-group/:id/alert-rule/:arid", auth(), user(), perm("/alert-rules/put"), alertRulePutByFE) - pages.GET("/alert-rule/:arid", auth(), user(), perm("/alert-rules"), alertRuleGet) - - pages.GET("/busi-group/:id/recording-rules", auth(), user(), perm("/recording-rules"), recordingRuleGets) - pages.POST("/busi-group/:id/recording-rules", auth(), user(), perm("/recording-rules/add"), bgrw(), recordingRuleAddByFE) - pages.DELETE("/busi-group/:id/recording-rules", auth(), user(), perm("/recording-rules/del"), bgrw(), recordingRuleDel) - pages.PUT("/busi-group/:id/recording-rule/:rrid", auth(), user(), perm("/recording-rules/put"), bgrw(), recordingRulePutByFE) - pages.GET("/recording-rule/:rrid", auth(), user(), perm("/recording-rules"), recordingRuleGet) - pages.PUT("/busi-group/:id/recording-rules/fields", auth(), user(), perm("/recording-rules/put"), recordingRulePutFields) - - pages.GET("/busi-group/:id/alert-mutes", auth(), user(), perm("/alert-mutes"), bgro(), alertMuteGetsByBG) - pages.POST("/busi-group/:id/alert-mutes", auth(), user(), perm("/alert-mutes/add"), bgrw(), alertMuteAdd) - pages.DELETE("/busi-group/:id/alert-mutes", auth(), user(), perm("/alert-mutes/del"), bgrw(), alertMuteDel) - pages.PUT("/busi-group/:id/alert-mute/:amid", auth(), user(), perm("/alert-mutes/put"), alertMutePutByFE) - pages.PUT("/busi-group/:id/alert-mutes/fields", auth(), user(), perm("/alert-mutes/put"), bgrw(), alertMutePutFields) - - pages.GET("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes"), bgro(), alertSubscribeGets) - pages.GET("/alert-subscribe/:sid", auth(), user(), perm("/alert-subscribes"), alertSubscribeGet) - pages.POST("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes/add"), bgrw(), alertSubscribeAdd) - pages.PUT("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes/put"), bgrw(), alertSubscribePut) - pages.DELETE("/busi-group/:id/alert-subscribes", auth(), user(), perm("/alert-subscribes/del"), bgrw(), alertSubscribeDel) - - if config.C.AnonymousAccess.AlertDetail { - pages.GET("/alert-cur-event/:eid", alertCurEventGet) - pages.GET("/alert-his-event/:eid", alertHisEventGet) - } else { - pages.GET("/alert-cur-event/:eid", auth(), alertCurEventGet) - pages.GET("/alert-his-event/:eid", auth(), alertHisEventGet) - } - - // card logic - pages.GET("/alert-cur-events/list", auth(), alertCurEventsList) - pages.GET("/alert-cur-events/card", auth(), alertCurEventsCard) - pages.POST("/alert-cur-events/card/details", auth(), alertCurEventsCardDetails) - pages.GET("/alert-his-events/list", auth(), alertHisEventsList) - pages.DELETE("/alert-cur-events", auth(), user(), perm("/alert-cur-events/del"), alertCurEventDel) - - pages.GET("/alert-aggr-views", auth(), alertAggrViewGets) - pages.DELETE("/alert-aggr-views", auth(), user(), alertAggrViewDel) - pages.POST("/alert-aggr-views", auth(), user(), alertAggrViewAdd) - pages.PUT("/alert-aggr-views", auth(), user(), alertAggrViewPut) - - pages.GET("/busi-group/:id/task-tpls", auth(), user(), perm("/job-tpls"), bgro(), taskTplGets) - pages.POST("/busi-group/:id/task-tpls", auth(), user(), perm("/job-tpls/add"), bgrw(), taskTplAdd) - pages.DELETE("/busi-group/:id/task-tpl/:tid", auth(), user(), perm("/job-tpls/del"), bgrw(), taskTplDel) - pages.POST("/busi-group/:id/task-tpls/tags", auth(), user(), perm("/job-tpls/put"), bgrw(), taskTplBindTags) - pages.DELETE("/busi-group/:id/task-tpls/tags", auth(), user(), perm("/job-tpls/put"), bgrw(), taskTplUnbindTags) - pages.GET("/busi-group/:id/task-tpl/:tid", auth(), user(), perm("/job-tpls"), bgro(), taskTplGet) - pages.PUT("/busi-group/:id/task-tpl/:tid", auth(), user(), perm("/job-tpls/put"), bgrw(), taskTplPut) - - pages.GET("/busi-group/:id/tasks", auth(), user(), perm("/job-tasks"), bgro(), taskGets) - pages.POST("/busi-group/:id/tasks", auth(), user(), perm("/job-tasks/add"), bgrw(), taskAdd) - pages.GET("/busi-group/:id/task/*url", auth(), user(), perm("/job-tasks"), taskProxy) - pages.PUT("/busi-group/:id/task/*url", auth(), user(), perm("/job-tasks/put"), bgrw(), taskProxy) - - pages.GET("/servers", auth(), admin(), serversGet) - pages.PUT("/server/:id", auth(), admin(), serverBindCluster) - pages.POST("/servers", auth(), admin(), serverAddCluster) - pages.DELETE("/servers", auth(), admin(), serverDelCluster) - } - - service := r.Group("/v1/n9e") - if len(config.C.BasicAuth) > 0 { - service.Use(gin.BasicAuth(config.C.BasicAuth)) - } - { - service.Any("/prometheus/*url", prometheusProxy) - service.POST("/users", userAddPost) - service.GET("/users", userFindAll) - - service.GET("/targets", targetGets) - service.GET("/targets/tags", targetGetTags) - service.POST("/targets/tags", targetBindTagsByService) - service.DELETE("/targets/tags", targetUnbindTagsByService) - service.PUT("/targets/note", targetUpdateNoteByService) - - service.POST("/alert-rules", alertRuleAddByService) - service.DELETE("/alert-rules", alertRuleDelByService) - service.PUT("/alert-rule/:arid", alertRulePutByService) - service.GET("/alert-rule/:arid", alertRuleGet) - service.GET("/alert-rules", alertRulesGetByService) - - service.GET("/alert-mutes", alertMuteGets) - service.POST("/alert-mutes", alertMuteAddByService) - service.DELETE("/alert-mutes", alertMuteDel) - - service.GET("/alert-cur-events", alertCurEventsList) - service.GET("/alert-his-events", alertHisEventsList) - service.GET("/alert-his-event/:eid", alertHisEventGet) - - service.GET("/config/:id", configGet) - service.GET("/configs", configsGet) - service.PUT("/configs", configsPut) - service.POST("/configs", configsPost) - service.DELETE("/configs", configsDel) - - service.POST("/conf-prop/encrypt", confPropEncrypt) - service.POST("/conf-prop/decrypt", confPropDecrypt) - } -} diff --git a/src/webapi/router/router_builtin.go b/src/webapi/router/router_builtin.go deleted file mode 100644 index 578c2389602272e1688faec183e826d04f88a325..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_builtin.go +++ /dev/null @@ -1,210 +0,0 @@ -package router - -import ( - "net/http" - "path" - "strings" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/webapi/config" - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/file" - "github.com/toolkits/pkg/ginx" - "github.com/toolkits/pkg/i18n" - "github.com/toolkits/pkg/runner" -) - -func alertRuleBuiltinList(c *gin.Context) { - fp := config.C.BuiltinAlertsDir - if fp == "" { - fp = path.Join(runner.Cwd, "etc", "alerts") - } - - files, err := file.FilesUnder(fp) - ginx.Dangerous(err) - - names := make([]string, 0, len(files)) - - for _, f := range files { - if !strings.HasSuffix(f, ".json") { - continue - } - - name := strings.TrimSuffix(f, ".json") - names = append(names, name) - } - - ginx.NewRender(c).Data(names, nil) -} - -type alertRuleBuiltinImportForm struct { - Name string `json:"name" binding:"required"` - Cluster string `json:"cluster" binding:"required"` -} - -func alertRuleBuiltinImport(c *gin.Context) { - var f alertRuleBuiltinImportForm - ginx.BindJSON(c, &f) - - dirpath := config.C.BuiltinAlertsDir - if dirpath == "" { - dirpath = path.Join(runner.Cwd, "etc", "alerts") - } - - jsonfile := path.Join(dirpath, f.Name+".json") - if !file.IsExist(jsonfile) { - ginx.Bomb(http.StatusBadRequest, "%s not found", jsonfile) - } - - var lst []models.AlertRule - ginx.Dangerous(file.ReadJson(jsonfile, &lst)) - - count := len(lst) - if count == 0 { - ginx.Bomb(http.StatusBadRequest, "builtin alerts is empty, file: %s", jsonfile) - } - - username := c.MustGet("username").(string) - bgid := ginx.UrlParamInt64(c, "id") - - // alert rule name -> error string - reterr := make(map[string]string) - for i := 0; i < count; i++ { - lst[i].Id = 0 - lst[i].Cluster = f.Cluster - lst[i].GroupId = bgid - lst[i].CreateBy = username - lst[i].UpdateBy = username - - if err := lst[i].FE2DB(); err != nil { - reterr[lst[i].Name] = i18n.Sprintf(c.GetHeader("X-Language"), err.Error()) - continue - } - - if err := lst[i].Add(); err != nil { - reterr[lst[i].Name] = i18n.Sprintf(c.GetHeader("X-Language"), err.Error()) - } else { - reterr[lst[i].Name] = "" - } - } - - ginx.NewRender(c).Data(reterr, nil) -} - -func builtinBoardGets(c *gin.Context) { - fp := config.C.BuiltinDashboardsDir - if fp == "" { - fp = path.Join(runner.Cwd, "etc", "dashboards") - } - - files, err := file.FilesUnder(fp) - ginx.Dangerous(err) - - names := make([]string, 0, len(files)) - - for _, f := range files { - if !strings.HasSuffix(f, ".json") { - continue - } - - name := strings.TrimSuffix(f, ".json") - names = append(names, name) - } - - ginx.NewRender(c).Data(names, nil) -} - -// read the json file content -func builtinBoardGet(c *gin.Context) { - name := ginx.UrlParamStr(c, "name") - dirpath := config.C.BuiltinDashboardsDir - if dirpath == "" { - dirpath = path.Join(runner.Cwd, "etc", "dashboards") - } - - jsonfile := path.Join(dirpath, name+".json") - if !file.IsExist(jsonfile) { - ginx.Bomb(http.StatusBadRequest, "%s not found", jsonfile) - } - - body, err := file.ReadString(jsonfile) - ginx.NewRender(c).Data(body, err) -} - -// deprecated ↓ - -type dashboardBuiltinImportForm struct { - Name string `json:"name" binding:"required"` -} - -func dashboardBuiltinImport(c *gin.Context) { - var f dashboardBuiltinImportForm - ginx.BindJSON(c, &f) - - dirpath := config.C.BuiltinDashboardsDir - if dirpath == "" { - dirpath = path.Join(runner.Cwd, "etc", "dashboards") - } - - jsonfile := path.Join(dirpath, f.Name+".json") - if !file.IsExist(jsonfile) { - ginx.Bomb(http.StatusBadRequest, "%s not found", jsonfile) - } - - var dashPures []DashboardPure - ginx.Dangerous(file.ReadJson(jsonfile, &dashPures)) - - me := c.MustGet("user").(*models.User) - bg := c.MustGet("busi_group").(*models.BusiGroup) - - ret := make(map[string]string) - - for _, dashPure := range dashPures { - dash := &models.Dashboard{ - Name: dashPure.Name, - Tags: dashPure.Tags, - Configs: dashPure.Configs, - GroupId: bg.Id, - CreateBy: me.Username, - UpdateBy: me.Username, - } - - ret[dash.Name] = "" - - err := dash.Add() - if err != nil { - ret[dash.Name] = i18n.Sprintf(c.GetHeader("X-Language"), err.Error()) - continue - } - - for _, cgPure := range dashPure.ChartGroups { - cg := &models.ChartGroup{ - Name: cgPure.Name, - Weight: cgPure.Weight, - DashboardId: dash.Id, - } - - err := cg.Add() - if err != nil { - ret[dash.Name] = err.Error() - continue - } - - for _, chartPure := range cgPure.Charts { - chart := &models.Chart{ - Configs: chartPure.Configs, - Weight: chartPure.Weight, - GroupId: cg.Id, - } - - err := chart.Add() - if err != nil { - ret[dash.Name] = err.Error() - continue - } - } - } - } - - ginx.NewRender(c).Data(ret, nil) -} diff --git a/src/webapi/router/router_chart.go b/src/webapi/router/router_chart.go deleted file mode 100644 index cb8573b0799a2cfeb3fb05b1b4398c1cd0f5c1e0..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_chart.go +++ /dev/null @@ -1,46 +0,0 @@ -package router - -import ( - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" -) - -func chartGets(c *gin.Context) { - lst, err := models.ChartsOf(ginx.QueryInt64(c, "cgid")) - ginx.NewRender(c).Data(lst, err) -} - -func chartAdd(c *gin.Context) { - var chart models.Chart - ginx.BindJSON(c, &chart) - - // group_id / configs / weight - chart.Id = 0 - err := chart.Add() - ginx.NewRender(c).Data(chart, err) -} - -func chartPut(c *gin.Context) { - var arr []models.Chart - ginx.BindJSON(c, &arr) - - for i := 0; i < len(arr); i++ { - ginx.Dangerous(arr[i].Update("configs", "weight", "group_id")) - } - - ginx.NewRender(c).Message(nil) -} - -func chartDel(c *gin.Context) { - var f idsForm - ginx.BindJSON(c, &f) - - for i := 0; i < len(f.Ids); i++ { - cg := models.Chart{Id: f.Ids[i]} - ginx.Dangerous(cg.Del()) - } - - ginx.NewRender(c).Message(nil) -} diff --git a/src/webapi/router/router_chart_group.go b/src/webapi/router/router_chart_group.go deleted file mode 100644 index 522f699746b44afdfd166415b5ba735c91c7ed91..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_chart_group.go +++ /dev/null @@ -1,46 +0,0 @@ -package router - -import ( - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" -) - -func chartGroupGets(c *gin.Context) { - objs, err := models.ChartGroupsOf(ginx.QueryInt64(c, "did")) - ginx.NewRender(c).Data(objs, err) -} - -func chartGroupAdd(c *gin.Context) { - var cg models.ChartGroup - ginx.BindJSON(c, &cg) - - // dashboard_id / name / weight - cg.Id = 0 - err := cg.Add() - ginx.NewRender(c).Data(cg, err) -} - -func chartGroupPut(c *gin.Context) { - var arr []models.ChartGroup - ginx.BindJSON(c, &arr) - - for i := 0; i < len(arr); i++ { - ginx.Dangerous(arr[i].Update("name", "weight", "dashboard_id")) - } - - ginx.NewRender(c).Message(nil) -} - -func chartGroupDel(c *gin.Context) { - var f idsForm - ginx.BindJSON(c, &f) - - for i := 0; i < len(f.Ids); i++ { - cg := models.ChartGroup{Id: f.Ids[i]} - ginx.Dangerous(cg.Del()) - } - - ginx.NewRender(c).Message(nil) -} diff --git a/src/webapi/router/router_config.go b/src/webapi/router/router_config.go deleted file mode 100644 index f0cd45ae7e1d26c085998563db969c5157bce34d..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_config.go +++ /dev/null @@ -1,16 +0,0 @@ -package router - -import ( - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/webapi/config" -) - -func notifyChannelsGets(c *gin.Context) { - ginx.NewRender(c).Data(config.C.NotifyChannels, nil) -} - -func contactKeysGets(c *gin.Context) { - ginx.NewRender(c).Data(config.C.ContactKeys, nil) -} diff --git a/src/webapi/router/router_dashboard.go b/src/webapi/router/router_dashboard.go deleted file mode 100644 index 1ac9bb44dec54ffdcac3bad38ec215e216560035..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_dashboard.go +++ /dev/null @@ -1,259 +0,0 @@ -package router - -import ( - "net/http" - "strings" - "time" - - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - "github.com/toolkits/pkg/i18n" - - "github.com/didi/nightingale/v5/src/models" -) - -// Return all, front-end search and paging -func dashboardGets(c *gin.Context) { - busiGroupId := ginx.UrlParamInt64(c, "id") - query := ginx.QueryStr(c, "query", "") - dashboards, err := models.DashboardGets(busiGroupId, query) - ginx.NewRender(c).Data(dashboards, err) -} - -type dashboardForm struct { - Name string `json:"name"` - Tags []string `json:"tags"` - Configs string `json:"configs"` - Pure bool `json:"pure"` // 更新的时候,如果pure=true,就不更新configs了 -} - -func dashboardAdd(c *gin.Context) { - var f dashboardForm - ginx.BindJSON(c, &f) - - me := c.MustGet("user").(*models.User) - - dash := &models.Dashboard{ - GroupId: ginx.UrlParamInt64(c, "id"), - Name: f.Name, - Tags: strings.Join(f.Tags, " "), - Configs: f.Configs, - CreateBy: me.Username, - UpdateBy: me.Username, - } - - err := dash.Add() - if err == nil { - models.NewDefaultChartGroup(dash.Id) - } - - ginx.NewRender(c).Message(err) -} - -func dashboardGet(c *gin.Context) { - dash := Dashboard(ginx.UrlParamInt64(c, "did")) - ginx.NewRender(c).Data(dash, nil) -} - -func dashboardPut(c *gin.Context) { - var f dashboardForm - ginx.BindJSON(c, &f) - - me := c.MustGet("user").(*models.User) - dash := Dashboard(ginx.UrlParamInt64(c, "did")) - - if dash.Name != f.Name { - exists, err := models.DashboardExists("name = ? and id <> ?", f.Name, dash.Id) - ginx.Dangerous(err) - - if exists { - ginx.Bomb(200, "Dashboard already exists") - } - } - - dash.Name = f.Name - dash.Tags = strings.Join(f.Tags, " ") - dash.TagsLst = f.Tags - dash.UpdateBy = me.Username - dash.UpdateAt = time.Now().Unix() - - var err error - if !f.Pure { - dash.Configs = f.Configs - err = dash.Update("name", "tags", "configs", "update_by", "update_at") - } else { - err = dash.Update("name", "tags", "update_by", "update_at") - } - - ginx.NewRender(c).Data(dash, err) -} - -func dashboardDel(c *gin.Context) { - dash := Dashboard(ginx.UrlParamInt64(c, "did")) - if dash.GroupId != ginx.UrlParamInt64(c, "id") { - ginx.Bomb(http.StatusForbidden, "Oops...bad boy...") - } - ginx.NewRender(c).Message(dash.Del()) -} - -type ChartPure struct { - Configs string `json:"configs"` - Weight int `json:"weight"` -} - -type ChartGroupPure struct { - Name string `json:"name"` - Weight int `json:"weight"` - Charts []ChartPure `json:"charts"` -} - -type DashboardPure struct { - Name string `json:"name"` - Tags string `json:"tags"` - Configs string `json:"configs"` - ChartGroups []ChartGroupPure `json:"chart_groups"` -} - -func dashboardExport(c *gin.Context) { - var f idsForm - ginx.BindJSON(c, &f) - - dashboards, err := models.DashboardGetsByIds(f.Ids) - ginx.Dangerous(err) - - dashPures := []DashboardPure{} - - for i := range dashboards { - // convert dashboard - dashPure := DashboardPure{ - Name: dashboards[i].Name, - Tags: dashboards[i].Tags, - Configs: dashboards[i].Configs, - } - - cgs, err := models.ChartGroupsOf(dashboards[i].Id) - ginx.Dangerous(err) - - cgPures := []ChartGroupPure{} - for j := range cgs { - cgPure := ChartGroupPure{ - Name: cgs[j].Name, - Weight: cgs[j].Weight, - } - - charts, err := models.ChartsOf(cgs[j].Id) - ginx.Dangerous(err) - - chartPures := []ChartPure{} - for k := range charts { - chartPure := ChartPure{ - Configs: charts[k].Configs, - Weight: charts[k].Weight, - } - chartPures = append(chartPures, chartPure) - } - - cgPure.Charts = chartPures - cgPures = append(cgPures, cgPure) - } - - dashPure.ChartGroups = cgPures - dashPures = append(dashPures, dashPure) - } - - ginx.NewRender(c).Data(dashPures, nil) -} - -func dashboardImport(c *gin.Context) { - var dashPures []DashboardPure - ginx.BindJSON(c, &dashPures) - - me := c.MustGet("user").(*models.User) - bg := c.MustGet("busi_group").(*models.BusiGroup) - - ret := make(map[string]string) - - for _, dashPure := range dashPures { - dash := &models.Dashboard{ - Name: dashPure.Name, - Tags: dashPure.Tags, - Configs: dashPure.Configs, - GroupId: bg.Id, - CreateBy: me.Username, - UpdateBy: me.Username, - } - - ret[dash.Name] = "" - - err := dash.Add() - if err != nil { - ret[dash.Name] = i18n.Sprintf(c.GetHeader("X-Language"), err.Error()) - continue - } - - for _, cgPure := range dashPure.ChartGroups { - cg := &models.ChartGroup{ - Name: cgPure.Name, - Weight: cgPure.Weight, - DashboardId: dash.Id, - } - - err := cg.Add() - if err != nil { - ret[dash.Name] = err.Error() - continue - } - - for _, chartPure := range cgPure.Charts { - chart := &models.Chart{ - Configs: chartPure.Configs, - Weight: chartPure.Weight, - GroupId: cg.Id, - } - - err := chart.Add() - if err != nil { - ret[dash.Name] = err.Error() - continue - } - } - } - } - - ginx.NewRender(c).Data(ret, nil) -} - -func dashboardClone(c *gin.Context) { - dash := Dashboard(ginx.UrlParamInt64(c, "did")) - user := c.MustGet("user").(*models.User) - - newDash := &models.Dashboard{ - Name: dash.Name + " Copy at " + time.Now().Format("2006-01-02 15:04:05"), - Tags: dash.Tags, - Configs: dash.Configs, - GroupId: dash.GroupId, - CreateBy: user.Username, - UpdateBy: user.Username, - } - ginx.Dangerous(newDash.Add()) - - chartGroups, err := models.ChartGroupsOf(dash.Id) - ginx.Dangerous(err) - - for _, chartGroup := range chartGroups { - charts, err := models.ChartsOf(chartGroup.Id) - ginx.Dangerous(err) - - chartGroup.DashboardId = newDash.Id - chartGroup.Id = 0 - ginx.Dangerous(chartGroup.Add()) - - for _, chart := range charts { - chart.Id = 0 - chart.GroupId = chartGroup.Id - ginx.Dangerous(chart.Add()) - } - } - - ginx.NewRender(c).Message(nil) -} diff --git a/src/webapi/router/router_funcs.go b/src/webapi/router/router_funcs.go deleted file mode 100644 index 58498e0e5b6766b0f61ac6708b8fedf489eb44e0..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_funcs.go +++ /dev/null @@ -1,150 +0,0 @@ -package router - -import ( - "fmt" - "net/http" - "strings" - - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - "github.com/toolkits/pkg/str" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/ibex" - "github.com/didi/nightingale/v5/src/webapi/config" -) - -const defaultLimit = 300 - -func queryClusters(c *gin.Context) []string { - clusters := ginx.QueryStr(c, "clusters", "") - clusters = strings.ReplaceAll(clusters, ",", " ") - return strings.Fields(clusters) -} - -func Cluster(c *gin.Context) string { - return c.GetHeader("X-Cluster") -} - -func MustGetCluster(c *gin.Context) string { - cluster := Cluster(c) - if cluster == "" { - ginx.Bomb(http.StatusBadRequest, "Header(X-Cluster) missed") - } - return cluster -} - -type idsForm struct { - Ids []int64 `json:"ids"` -} - -func (f idsForm) Verify() { - if len(f.Ids) == 0 { - ginx.Bomb(http.StatusBadRequest, "ids empty") - } -} - -func User(id int64) *models.User { - obj, err := models.UserGetById(id) - ginx.Dangerous(err) - - if obj == nil { - ginx.Bomb(http.StatusNotFound, "No such user") - } - - return obj -} - -func UserGroup(id int64) *models.UserGroup { - obj, err := models.UserGroupGetById(id) - ginx.Dangerous(err) - - if obj == nil { - ginx.Bomb(http.StatusNotFound, "No such UserGroup") - } - - return obj -} - -func BusiGroup(id int64) *models.BusiGroup { - obj, err := models.BusiGroupGetById(id) - ginx.Dangerous(err) - - if obj == nil { - ginx.Bomb(http.StatusNotFound, "No such BusiGroup") - } - - return obj -} - -func Dashboard(id int64) *models.Dashboard { - obj, err := models.DashboardGet("id=?", id) - ginx.Dangerous(err) - - if obj == nil { - ginx.Bomb(http.StatusNotFound, "No such dashboard") - } - - return obj -} - -type DoneIdsReply struct { - Err string `json:"err"` - Dat struct { - List []int64 `json:"list"` - } `json:"dat"` -} - -func TaskDoneIds(ids []int64) ([]int64, error) { - var res DoneIdsReply - err := ibex.New( - config.C.Ibex.Address, - config.C.Ibex.BasicAuthUser, - config.C.Ibex.BasicAuthPass, - config.C.Ibex.Timeout, - ). - Path("/ibex/v1/tasks/done-ids"). - QueryString("ids", str.IdsString(ids, ",")). - Out(&res). - GET() - - if err != nil { - return nil, err - } - - if res.Err != "" { - return nil, fmt.Errorf("response.err: %v", res.Err) - } - - return res.Dat.List, nil -} - -type TaskCreateReply struct { - Err string `json:"err"` - Dat int64 `json:"dat"` // task.id -} - -// return task.id, error -func TaskCreate(v interface{}) (int64, error) { - var res TaskCreateReply - err := ibex.New( - config.C.Ibex.Address, - config.C.Ibex.BasicAuthUser, - config.C.Ibex.BasicAuthPass, - config.C.Ibex.Timeout, - ). - Path("/ibex/v1/tasks"). - In(v). - Out(&res). - POST() - - if err != nil { - return 0, err - } - - if res.Err != "" { - return 0, fmt.Errorf("response.err: %v", res.Err) - } - - return res.Dat, nil -} diff --git a/src/webapi/router/router_prometheus.go b/src/webapi/router/router_prometheus.go deleted file mode 100644 index 9f3af6051a22c1cfa4c40c948471e7b0a2c9a37a..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_prometheus.go +++ /dev/null @@ -1,143 +0,0 @@ -package router - -import ( - "context" - - "net/http" - "net/http/httputil" - "net/url" - "strings" - "time" - - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - pkgprom "github.com/didi/nightingale/v5/src/pkg/prom" - "github.com/didi/nightingale/v5/src/webapi/config" - "github.com/didi/nightingale/v5/src/webapi/prom" - "github.com/prometheus/common/model" -) - -type queryFormItem struct { - Start int64 `json:"start" binding:"required"` - End int64 `json:"end" binding:"required"` - Step int64 `json:"step" binding:"required"` - Query string `json:"query" binding:"required"` -} - -type batchQueryForm struct { - Queries []queryFormItem `json:"queries" binding:"required"` -} - -func promBatchQueryRange(c *gin.Context) { - xcluster := c.GetHeader("X-Cluster") - if xcluster == "" { - ginx.Bomb(http.StatusBadRequest, "header(X-Cluster) is blank") - } - - var f batchQueryForm - ginx.Dangerous(c.BindJSON(&f)) - - cluster, exist := prom.Clusters.Get(xcluster) - if !exist { - ginx.Bomb(http.StatusBadRequest, "cluster(%s) not found", xcluster) - } - - var lst []model.Value - - for _, item := range f.Queries { - r := pkgprom.Range{ - Start: time.Unix(item.Start, 0), - End: time.Unix(item.End, 0), - Step: time.Duration(item.Step) * time.Second, - } - - resp, _, err := cluster.PromClient.QueryRange(context.Background(), item.Query, r) - ginx.Dangerous(err) - - lst = append(lst, resp) - } - - ginx.NewRender(c).Data(lst, nil) -} - -func prometheusProxy(c *gin.Context) { - xcluster := c.GetHeader("X-Cluster") - if xcluster == "" { - c.String(http.StatusBadRequest, "X-Cluster missed") - return - } - - cluster, exists := prom.Clusters.Get(xcluster) - if !exists { - c.String(http.StatusBadRequest, "No such cluster: %s", xcluster) - return - } - - target, err := url.Parse(cluster.Opts.Prom) - if err != nil { - c.String(http.StatusInternalServerError, "invalid prometheus url: %s", cluster.Opts.Prom) - return - } - - director := func(req *http.Request) { - req.URL.Scheme = target.Scheme - req.URL.Host = target.Host - req.Host = target.Host - - req.Header.Set("Host", target.Host) - - // fe request e.g. /api/n9e/prometheus/api/v1/query - index := strings.Index(req.URL.Path, "/prometheus") - if index == -1 { - panic("url path invalid") - } - - req.URL.Path = strings.TrimRight(target.Path, "/") + req.URL.Path[index+11:] - - if target.RawQuery == "" || req.URL.RawQuery == "" { - req.URL.RawQuery = target.RawQuery + req.URL.RawQuery - } else { - req.URL.RawQuery = target.RawQuery + "&" + req.URL.RawQuery - } - - if _, ok := req.Header["User-Agent"]; !ok { - req.Header.Set("User-Agent", "") - } - - if cluster.Opts.BasicAuthUser != "" { - req.SetBasicAuth(cluster.Opts.BasicAuthUser, cluster.Opts.BasicAuthPass) - } - - headerCount := len(cluster.Opts.Headers) - if headerCount > 0 && headerCount%2 == 0 { - for i := 0; i < len(cluster.Opts.Headers); i += 2 { - req.Header.Set(cluster.Opts.Headers[i], cluster.Opts.Headers[i+1]) - if cluster.Opts.Headers[i] == "Host" { - req.Host = cluster.Opts.Headers[i+1] - } - } - } - } - - errFunc := func(w http.ResponseWriter, r *http.Request, err error) { - http.Error(w, err.Error(), http.StatusBadGateway) - } - - proxy := &httputil.ReverseProxy{ - Director: director, - Transport: cluster.Transport, - ErrorHandler: errFunc, - } - - proxy.ServeHTTP(c.Writer, c.Request) -} - -func clustersGets(c *gin.Context) { - count := len(config.C.Clusters) - names := make([]string, 0, count) - for i := 0; i < count; i++ { - names = append(names, config.C.Clusters[i].Name) - } - ginx.NewRender(c).Data(names, nil) -} diff --git a/src/webapi/router/router_role.go b/src/webapi/router/router_role.go deleted file mode 100644 index dae2cdd3b318a8fa661bad29102a1c547a1709cc..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_role.go +++ /dev/null @@ -1,21 +0,0 @@ -package router - -import ( - "strings" - - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" -) - -func rolesGets(c *gin.Context) { - lst, err := models.RoleGetsAll() - ginx.NewRender(c).Data(lst, err) -} - -func permsGets(c *gin.Context) { - user := c.MustGet("user").(*models.User) - lst, err := models.OperationsOfRole(strings.Fields(user.Roles)) - ginx.NewRender(c).Data(lst, err) -} diff --git a/src/webapi/router/router_server.go b/src/webapi/router/router_server.go deleted file mode 100644 index c900c20ba6518029d9f4b4132e5657cc9cc9fe7b..0000000000000000000000000000000000000000 --- a/src/webapi/router/router_server.go +++ /dev/null @@ -1,51 +0,0 @@ -package router - -import ( - "github.com/didi/nightingale/v5/src/models" - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" -) - -// 页面上,拉取 server 列表 -func serversGet(c *gin.Context) { - list, err := models.AlertingEngineGets("") - ginx.NewRender(c).Data(list, err) -} - -type serverBindClusterForm struct { - Cluster string `json:"cluster"` - Instance string `json:"instance"` -} - -// 用户为某个 n9e-server 分配一个集群,也可以清空,设置cluster为空字符串即可 -// 清空就表示这个server没啥用了,可能是要下线掉,或者仅仅用作转发器 -func serverBindCluster(c *gin.Context) { - id := ginx.UrlParamInt64(c, "id") - - ae, err := models.AlertingEngineGet("id = ?", id) - ginx.Dangerous(err) - - if ae == nil { - ginx.Dangerous("no such server") - } - - var f serverBindClusterForm - ginx.BindJSON(c, &f) - - ginx.NewRender(c).Message(ae.UpdateCluster(f.Cluster)) -} - -func serverAddCluster(c *gin.Context) { - var f serverBindClusterForm - ginx.BindJSON(c, &f) - - ginx.NewRender(c).Message(models.AlertingEngineAdd(f.Instance, f.Cluster)) -} - -func serverDelCluster(c *gin.Context) { - var f idsForm - ginx.BindJSON(c, &f) - f.Verify() - - ginx.NewRender(c).Message(models.AlertingEngineDel(f.Ids)) -} diff --git a/src/webapi/webapi.go b/src/webapi/webapi.go deleted file mode 100644 index b90eecf87b30f931bd40883f86f436a0c5d1473c..0000000000000000000000000000000000000000 --- a/src/webapi/webapi.go +++ /dev/null @@ -1,149 +0,0 @@ -package webapi - -import ( - "fmt" - "os" - "os/signal" - "path/filepath" - "syscall" - - "github.com/toolkits/pkg/i18n" - - "github.com/didi/nightingale/v5/src/models" - "github.com/didi/nightingale/v5/src/pkg/cas" - "github.com/didi/nightingale/v5/src/pkg/httpx" - "github.com/didi/nightingale/v5/src/pkg/ldapx" - "github.com/didi/nightingale/v5/src/pkg/logx" - "github.com/didi/nightingale/v5/src/pkg/oauth2x" - "github.com/didi/nightingale/v5/src/pkg/oidcc" - "github.com/didi/nightingale/v5/src/storage" - "github.com/didi/nightingale/v5/src/webapi/config" - "github.com/didi/nightingale/v5/src/webapi/prom" - "github.com/didi/nightingale/v5/src/webapi/router" - "github.com/didi/nightingale/v5/src/webapi/stat" -) - -type Webapi struct { - ConfigFile string - Version string - Key string -} - -type WebapiOption func(*Webapi) - -func SetConfigFile(f string) WebapiOption { - return func(s *Webapi) { - s.ConfigFile = f - } -} - -func SetVersion(v string) WebapiOption { - return func(s *Webapi) { - s.Version = v - } -} - -func SetKey(k string) WebapiOption { - return func(s *Webapi) { - s.Key = k - } -} - -// Run run webapi -func Run(opts ...WebapiOption) { - code := 1 - sc := make(chan os.Signal, 1) - signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - - webapi := Webapi{ - ConfigFile: filepath.Join("etc", "webapi.conf"), - Version: "not specified", - } - - for _, opt := range opts { - opt(&webapi) - } - - cleanFunc, err := webapi.initialize() - if err != nil { - fmt.Println("webapi init fail:", err) - os.Exit(code) - } - -EXIT: - for { - sig := <-sc - fmt.Println("received signal:", sig.String()) - switch sig { - case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: - code = 0 - break EXIT - case syscall.SIGHUP: - // reload configuration? - default: - break EXIT - } - } - - cleanFunc() - fmt.Println("webapi exited") - os.Exit(code) -} - -func (a Webapi) initialize() (func(), error) { - // parse config file - config.MustLoad(a.Key, a.ConfigFile) - - // init i18n - i18n.Init(config.C.I18N) - - // init ldap - ldapx.Init(config.C.LDAP) - - // init oidc - oidcc.Init(config.C.OIDC) - - // init cas - cas.Init(config.C.CAS) - - // init oauth - oauth2x.Init(config.C.OAuth) - - // init logger - loggerClean, err := logx.Init(config.C.Log) - if err != nil { - return nil, err - } - - // init database - if err = storage.InitDB(config.C.DB); err != nil { - return nil, err - } - - // init redis - redisClean, err := storage.InitRedis(config.C.Redis) - if err != nil { - return nil, err - } - - models.InitSalt() - models.InitRoot() - - // init prometheus proxy config - if err = prom.Init(); err != nil { - return nil, err - } - - stat.Init() - - // init http server - r := router.New(a.Version) - httpClean := httpx.Init(config.C.HTTP, r) - - // release all the resources - return func() { - loggerClean() - httpClean() - redisClean() - }, nil -} diff --git a/src/storage/storage.go b/storage/redis.go similarity index 79% rename from src/storage/storage.go rename to storage/redis.go index a7218ab5cef53939820e6df0e8c4d822e7c7ef0b..dc2eef901132ae472c952cd65be0e627fccee776 100644 --- a/src/storage/storage.go +++ b/storage/redis.go @@ -7,10 +7,9 @@ import ( "strings" "time" - "github.com/didi/nightingale/v5/src/pkg/ormx" - "github.com/didi/nightingale/v5/src/pkg/tls" - "github.com/go-redis/redis/v9" - "gorm.io/gorm" + "github.com/ccfos/nightingale/v6/pkg/tlsx" + + "github.com/redis/go-redis/v9" ) type RedisConfig struct { @@ -19,24 +18,14 @@ type RedisConfig struct { Password string DB int UseTLS bool - tls.ClientConfig + tlsx.ClientConfig RedisType string MasterName string SentinelUsername string SentinelPassword string } -var DB *gorm.DB - -func InitDB(cfg ormx.DBConfig) error { - db, err := ormx.New(cfg) - if err == nil { - DB = db - } - return err -} - -var Redis interface { +type Redis interface { Del(ctx context.Context, keys ...string) *redis.IntCmd Get(ctx context.Context, key string) *redis.StringCmd Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd @@ -48,7 +37,8 @@ var Redis interface { Publish(ctx context.Context, channel string, message interface{}) *redis.IntCmd } -func InitRedis(cfg RedisConfig) (func(), error) { +func NewRedis(cfg RedisConfig) (Redis, error) { + var redisClient Redis switch cfg.RedisType { case "standalone", "": redisOptions := &redis.Options{ @@ -67,7 +57,7 @@ func InitRedis(cfg RedisConfig) (func(), error) { redisOptions.TLSConfig = tlsConfig } - Redis = redis.NewClient(redisOptions) + redisClient = redis.NewClient(redisOptions) case "cluster": redisOptions := &redis.ClusterOptions{ @@ -85,7 +75,7 @@ func InitRedis(cfg RedisConfig) (func(), error) { redisOptions.TLSConfig = tlsConfig } - Redis = redis.NewClusterClient(redisOptions) + redisClient = redis.NewClusterClient(redisOptions) case "sentinel": redisOptions := &redis.FailoverOptions{ @@ -107,21 +97,17 @@ func InitRedis(cfg RedisConfig) (func(), error) { redisOptions.TLSConfig = tlsConfig } - Redis = redis.NewFailoverClient(redisOptions) + redisClient = redis.NewFailoverClient(redisOptions) default: fmt.Println("failed to init redis , redis type is illegal:", cfg.RedisType) os.Exit(1) } - err := Redis.Ping(context.Background()).Err() + err := redisClient.Ping(context.Background()).Err() if err != nil { fmt.Println("failed to ping redis:", err) os.Exit(1) } - - return func() { - fmt.Println("redis exiting") - Redis.Close() - }, nil + return redisClient, nil } diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000000000000000000000000000000000000..b548477fd53b69e144af488af040b0bccd222011 --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,16 @@ +package storage + +import ( + "github.com/ccfos/nightingale/v6/pkg/ormx" + + "gorm.io/gorm" +) + +func New(cfg ormx.DBConfig) (*gorm.DB, error) { + db, err := ormx.New(cfg) + if err != nil { + return nil, err + } + + return db, nil +}