Copy disabled (too large)
Download .txt
Showing preview only (10,240K chars total). Download the full file to get everything.
Repository: ccfos/nightingale
Branch: main
Commit: ea9c52c808cd
Files: 802
Total size: 9.6 MB
Directory structure:
gitextract_wwt_h4rf/
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── config.yml
│ │ ├── enhancement.md
│ │ └── question.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── issue-translator.yml
│ └── n9e.yml
├── .gitignore
├── .goreleaser.yaml
├── .typos.toml
├── LICENSE
├── Makefile
├── README.md
├── README_zh.md
├── alert/
│ ├── aconf/
│ │ └── conf.go
│ ├── alert.go
│ ├── astats/
│ │ └── stats.go
│ ├── common/
│ │ └── key.go
│ ├── dispatch/
│ │ ├── consume.go
│ │ ├── dispatch.go
│ │ ├── log.go
│ │ ├── notify_channel.go
│ │ └── notify_target.go
│ ├── eval/
│ │ ├── alert_rule.go
│ │ ├── eval.go
│ │ └── eval_test.go
│ ├── mute/
│ │ └── mute.go
│ ├── naming/
│ │ ├── hashring.go
│ │ ├── heartbeat.go
│ │ └── leader.go
│ ├── pipeline/
│ │ ├── engine/
│ │ │ └── engine.go
│ │ ├── pipeline.go
│ │ └── processor/
│ │ ├── aisummary/
│ │ │ ├── ai_summary.go
│ │ │ └── ai_summary_test.go
│ │ ├── callback/
│ │ │ └── callback.go
│ │ ├── common/
│ │ │ └── common.go
│ │ ├── eventdrop/
│ │ │ └── event_drop.go
│ │ ├── eventupdate/
│ │ │ └── event_update.go
│ │ ├── logic/
│ │ │ ├── if.go
│ │ │ └── switch.go
│ │ ├── relabel/
│ │ │ └── relabel.go
│ │ └── utils/
│ │ └── utils.go
│ ├── process/
│ │ ├── alert_cur_event.go
│ │ └── process.go
│ ├── queue/
│ │ └── queue.go
│ ├── record/
│ │ ├── prom_rule.go
│ │ ├── sample.go
│ │ └── scheduler.go
│ ├── router/
│ │ ├── router.go
│ │ ├── router_alert_eval_detail.go
│ │ ├── router_event.go
│ │ ├── router_event_detail.go
│ │ └── router_trace_logs.go
│ └── sender/
│ ├── callback.go
│ ├── dingtalk.go
│ ├── email.go
│ ├── feishu.go
│ ├── feishucard.go
│ ├── global_webhook.go
│ ├── global_webhook_test.go
│ ├── ibex.go
│ ├── lark.go
│ ├── larkcard.go
│ ├── mm.go
│ ├── notify_record_queue.go
│ ├── plugin.go
│ ├── plugin_cmd_unix.go
│ ├── plugin_cmd_windows.go
│ ├── sender.go
│ ├── telegram.go
│ ├── webhook.go
│ ├── webhook_event_queue.go
│ ├── webhook_event_queue_test.go
│ ├── webhook_queue.go
│ └── wecom.go
├── center/
│ ├── cconf/
│ │ ├── conf.go
│ │ ├── event_example.go
│ │ ├── metric.go
│ │ ├── ops.go
│ │ ├── plugin.go
│ │ ├── rsa/
│ │ │ └── rsa_conf.go
│ │ └── sql_tpl.go
│ ├── center.go
│ ├── cstats/
│ │ └── stats.go
│ ├── integration/
│ │ └── init.go
│ ├── metas/
│ │ └── metas.go
│ ├── router/
│ │ ├── router.go
│ │ ├── router_alert_aggr_view.go
│ │ ├── router_alert_cur_event.go
│ │ ├── router_alert_eval_detail.go
│ │ ├── router_alert_his_event.go
│ │ ├── router_alert_rule.go
│ │ ├── router_alert_subscribe.go
│ │ ├── router_board.go
│ │ ├── router_builtin.go
│ │ ├── router_builtin_component.go
│ │ ├── router_builtin_metric_filter.go
│ │ ├── router_builtin_metrics.go
│ │ ├── router_builtin_payload.go
│ │ ├── router_busi_group.go
│ │ ├── router_captcha.go
│ │ ├── router_chart_share.go
│ │ ├── router_config.go
│ │ ├── router_configs.go
│ │ ├── router_crypto.go
│ │ ├── router_dash_annotation.go
│ │ ├── router_dashboard.go
│ │ ├── router_datasource.go
│ │ ├── router_datasource_db.go
│ │ ├── router_embedded.go
│ │ ├── router_es.go
│ │ ├── router_es_index_pattern.go
│ │ ├── router_event_detail.go
│ │ ├── router_event_pipeline.go
│ │ ├── router_funcs.go
│ │ ├── router_heartbeat.go
│ │ ├── router_login.go
│ │ ├── router_message_template.go
│ │ ├── router_metric_desc.go
│ │ ├── router_metric_view.go
│ │ ├── router_mute.go
│ │ ├── router_mw.go
│ │ ├── router_notification_record.go
│ │ ├── router_notify_channel.go
│ │ ├── router_notify_channel_test.go
│ │ ├── router_notify_config.go
│ │ ├── router_notify_rule.go
│ │ ├── router_notify_tpl.go
│ │ ├── router_opensearch.go
│ │ ├── router_proxy.go
│ │ ├── router_query.go
│ │ ├── router_recording_rule.go
│ │ ├── router_role.go
│ │ ├── router_role_operation.go
│ │ ├── router_saved_view.go
│ │ ├── router_self.go
│ │ ├── router_server.go
│ │ ├── router_source_token.go
│ │ ├── router_target.go
│ │ ├── router_task.go
│ │ ├── router_task_tpl.go
│ │ ├── router_tdengine.go
│ │ ├── router_trace_logs.go
│ │ ├── router_user.go
│ │ ├── router_user_group.go
│ │ └── router_user_variable_config.go
│ └── sso/
│ ├── init.go
│ └── sync.go
├── cli/
│ ├── cli.go
│ └── upgrade/
│ ├── config.go
│ ├── readme.md
│ ├── upgrade.go
│ └── upgrade.sql
├── cmd/
│ ├── alert/
│ │ └── main.go
│ ├── center/
│ │ └── main.go
│ ├── cli/
│ │ └── main.go
│ ├── edge/
│ │ ├── edge.go
│ │ └── main.go
│ └── pushgw/
│ └── main.go
├── conf/
│ ├── conf.go
│ └── crypto.go
├── cron/
│ ├── clean_notify_record.go
│ └── clean_pipeline_execution.go
├── datasource/
│ ├── ck/
│ │ └── clickhouse.go
│ ├── commons/
│ │ └── eslike/
│ │ └── eslike.go
│ ├── datasource.go
│ ├── doris/
│ │ └── doris.go
│ ├── es/
│ │ └── es.go
│ ├── mysql/
│ │ └── mysql.go
│ ├── opensearch/
│ │ └── opensearch.go
│ ├── postgresql/
│ │ └── postgresql.go
│ ├── prom/
│ │ └── prom.go
│ ├── tdengine/
│ │ └── tdengine.go
│ └── victorialogs/
│ └── victorialogs.go
├── doc/
│ ├── README.bak.md
│ ├── active-contributors.md
│ ├── committers.md
│ ├── community-governance.md
│ ├── contributors.md
│ ├── end-users.md
│ ├── pmc.md
│ └── server-dash.json
├── docker/
│ ├── .dockerignore
│ ├── Dockerfile.goreleaser
│ ├── Dockerfile.goreleaser.arm64
│ ├── build.sh
│ ├── compose-bridge/
│ │ ├── docker-compose.yaml
│ │ ├── etc-categraf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.mysql/
│ │ │ │ └── mysql.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ ├── input.prometheus/
│ │ │ │ └── prometheus.toml
│ │ │ ├── input.redis/
│ │ │ │ └── redis.toml
│ │ │ └── input.system/
│ │ │ └── system.toml
│ │ ├── etc-mysql/
│ │ │ └── my.cnf
│ │ └── etc-nightingale/
│ │ ├── config.toml
│ │ ├── metrics.yaml
│ │ └── script/
│ │ ├── notify.bak.py
│ │ ├── notify.py
│ │ ├── notify_feishu.py
│ │ └── rule_converter.py
│ ├── compose-host-network/
│ │ ├── docker-compose.yaml
│ │ ├── etc-categraf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ └── input.system/
│ │ │ └── system.toml
│ │ ├── etc-mysql/
│ │ │ └── my.cnf
│ │ ├── etc-nightingale/
│ │ │ ├── config.toml
│ │ │ ├── metrics.yaml
│ │ │ └── script/
│ │ │ ├── notify.bak.py
│ │ │ ├── notify.py
│ │ │ ├── notify_feishu.py
│ │ │ └── rule_converter.py
│ │ └── etc-prometheus/
│ │ └── prometheus.yml
│ ├── compose-host-network-metric-log/
│ │ ├── docker-compose.yaml
│ │ ├── etc-categraf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ ├── input.system/
│ │ │ │ └── system.toml
│ │ │ └── logs.toml
│ │ ├── etc-logstash/
│ │ │ └── logstash.yaml
│ │ ├── etc-mysql/
│ │ │ └── my.cnf
│ │ ├── etc-nightingale/
│ │ │ ├── config.toml
│ │ │ ├── metrics.yaml
│ │ │ └── script/
│ │ │ ├── notify.bak.py
│ │ │ ├── notify.py
│ │ │ ├── notify_feishu.py
│ │ │ └── rule_converter.py
│ │ └── etc-prometheus/
│ │ └── prometheus.yml
│ ├── compose-postgres/
│ │ ├── categraf/
│ │ │ └── conf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.docker/
│ │ │ │ └── docker.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ ├── input.system/
│ │ │ │ └── system.toml
│ │ │ └── prometheus.toml
│ │ ├── docker-compose.yaml
│ │ ├── initsql_for_postgres/
│ │ │ ├── a-n9e-for-Postgres.sql
│ │ │ └── b-ibex-for-Postgres.sql
│ │ ├── n9eetc_pg/
│ │ │ ├── config.toml
│ │ │ └── metrics.yaml
│ │ └── prometc_vm/
│ │ ├── prometheus.yml
│ │ └── targets.json
│ ├── initsql/
│ │ ├── a-n9e.sql
│ │ └── c-init.sql
│ ├── migratesql/
│ │ └── migrate.sql
│ └── sqlite.sql
├── dscache/
│ ├── cache.go
│ └── sync.go
├── dskit/
│ ├── clickhouse/
│ │ ├── clickhouse.go
│ │ ├── clickhouse_test.go
│ │ └── timeseries.go
│ ├── doris/
│ │ ├── doris.go
│ │ ├── logs.go
│ │ ├── sql_analyzer.go
│ │ ├── sql_analyzer_test.go
│ │ ├── template.md
│ │ └── timeseries.go
│ ├── mysql/
│ │ ├── mysql.go
│ │ ├── mysql_test.go
│ │ ├── timeseries.go
│ │ └── timeseries_test.go
│ ├── pool/
│ │ └── pool.go
│ ├── postgres/
│ │ ├── postgres.go
│ │ └── timeseries.go
│ ├── sqlbase/
│ │ ├── base.go
│ │ ├── timeseries.go
│ │ └── timeseries_test.go
│ ├── tdengine/
│ │ └── tdengine.go
│ ├── types/
│ │ ├── timeseries.go
│ │ └── types.go
│ └── victorialogs/
│ ├── victorialogs.go
│ └── victorialogs_test.go
├── dumper/
│ ├── dumper.go
│ └── sync.go
├── etc/
│ ├── config.toml
│ ├── edge/
│ │ └── edge.toml
│ ├── metrics.yaml
│ └── script/
│ ├── notify.bak.py
│ ├── notify.py
│ ├── notify_feishu.py
│ └── rule_converter.py
├── fe.sh
├── go.mod
├── go.sum
├── integrations/
│ ├── AMD_ROCm_SMI/
│ │ ├── collect/
│ │ │ └── amd_rocm_smi/
│ │ │ └── rocm.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── AliYun/
│ │ ├── collect/
│ │ │ └── aliyun/
│ │ │ └── cloud.toml
│ │ ├── dashboards/
│ │ │ ├── arms-api.json
│ │ │ ├── arms-application.json
│ │ │ ├── arms-db.json
│ │ │ ├── arms-jvm-service.json
│ │ │ ├── arms-machine.json
│ │ │ ├── arms_jvm.json
│ │ │ ├── cdn.json
│ │ │ ├── ecs.json
│ │ │ ├── mongodb.json
│ │ │ ├── mse.json
│ │ │ ├── mysql.json
│ │ │ ├── nat.json
│ │ │ ├── oss.json
│ │ │ ├── polardb_mysql.json
│ │ │ ├── rds.json
│ │ │ ├── rds_new.json
│ │ │ ├── redis.json
│ │ │ ├── redis_cluster.json
│ │ │ ├── redis_new.json
│ │ │ ├── redis_standard.json
│ │ │ ├── slb.json
│ │ │ ├── slb_new.json
│ │ │ └── waf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── AppDynamics/
│ │ ├── collect/
│ │ │ └── appdynamics/
│ │ │ └── app.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── AutoMQ/
│ │ ├── alerts/
│ │ │ └── 常用告警规则.json
│ │ ├── collect/
│ │ │ └── prometheus/
│ │ │ └── 采集OTEL-COLLECTOR的样例.toml
│ │ ├── dashboards/
│ │ │ ├── broker_metrics.json
│ │ │ ├── cluster_overview.json
│ │ │ ├── detailed_metrics.json
│ │ │ ├── group_metrics.json
│ │ │ └── topic_metrics.json
│ │ ├── markdown/
│ │ │ └── overview.md
│ │ └── metrics/
│ │ └── exporter.json
│ ├── Bind/
│ │ ├── collect/
│ │ │ └── bind/
│ │ │ └── bind.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Canal/
│ │ ├── dashboards/
│ │ │ └── canal_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Ceph/
│ │ ├── alerts/
│ │ │ └── ceph_by_categraf.json
│ │ ├── dashboards/
│ │ │ └── ceph_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── ClickHouse/
│ │ ├── alerts/
│ │ │ ├── clickhouse_by_categraf.json
│ │ │ └── clickhouse_by_exporter.json
│ │ ├── collect/
│ │ │ └── clickhouse/
│ │ │ └── clickhouse.toml
│ │ ├── dashboards/
│ │ │ ├── clickhouse_by_categraf.json
│ │ │ └── clickhouse_by_exporter.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ ├── clickhouse_by_categraf.json
│ │ └── clickhouse_by_exporter.json
│ ├── CloudWatch/
│ │ ├── collect/
│ │ │ └── cloudwatch/
│ │ │ └── cloud.toml
│ │ ├── dashboards/
│ │ │ └── dashboard-by-aws-rds.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Consul/
│ │ ├── collect/
│ │ │ └── consul/
│ │ │ └── consul.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Dns_Query/
│ │ ├── collect/
│ │ │ └── dns_query/
│ │ │ └── dns_query.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Docker/
│ │ ├── collect/
│ │ │ └── docker/
│ │ │ └── docker.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Doris/
│ │ ├── alerts/
│ │ │ └── doris_by_categraf.json
│ │ ├── collect/
│ │ │ └── prometheus/
│ │ │ └── collect_doris_examples.toml
│ │ ├── dashboards/
│ │ │ └── Doris_Overview.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Elasticsearch/
│ │ ├── alerts/
│ │ │ ├── elasticsearch_by_categraf.json
│ │ │ └── elasticsearch_by_exporter.json
│ │ ├── collect/
│ │ │ └── elasticsearch/
│ │ │ └── elasticsearch.toml
│ │ ├── dashboards/
│ │ │ ├── elasticsearch_by_categraf.json
│ │ │ ├── elasticsearch_by_categraf_0.3.102.json
│ │ │ ├── elasticsearch_by_categraf_a.json
│ │ │ ├── elasticsearch_by_categraf_b.json
│ │ │ └── elasticsearch_by_exporter.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf-base.json
│ ├── Exec/
│ │ ├── collect/
│ │ │ └── exec/
│ │ │ └── exec.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Filecount/
│ │ ├── collect/
│ │ │ └── filecount/
│ │ │ └── filecount.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Gitlab/
│ │ ├── alerts/
│ │ │ └── gitlab_by_categraf.json
│ │ ├── dashboards/
│ │ │ ├── MachinePerformance.json
│ │ │ ├── NGINXVTS.json
│ │ │ ├── Overview.json
│ │ │ ├── PostgreSQL.json
│ │ │ └── Redis.json
│ │ └── markdown/
│ │ └── README.md
│ ├── GoogleCloud/
│ │ ├── collect/
│ │ │ └── googlecloud/
│ │ │ └── gcp.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── HAProxy/
│ │ ├── collect/
│ │ │ └── haproxy/
│ │ │ └── haproxy.toml
│ │ ├── dashboards/
│ │ │ └── dashboard.json
│ │ └── markdown/
│ │ └── README.md
│ ├── HTTP_Response/
│ │ ├── alerts/
│ │ │ └── http_response_by_categraf.json
│ │ ├── collect/
│ │ │ └── http_response/
│ │ │ └── http_response.toml
│ │ ├── dashboards/
│ │ │ └── http_response_by_categraf.json
│ │ ├── markdown/
│ │ │ └── http.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── IPMI/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── collect/
│ │ │ └── ipmi/
│ │ │ └── conf.toml
│ │ ├── dashboards/
│ │ │ ├── IPMI.json
│ │ │ ├── IPMI_by_categraf.json
│ │ │ └── IPMI_by_prometheus.json
│ │ └── markdown/
│ │ └── README.md
│ ├── IPVS/
│ │ ├── collect/
│ │ │ └── ipvs/
│ │ │ └── ipvs.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Java/
│ │ └── dashboards/
│ │ ├── jmx_by_exporter.json
│ │ ├── jmx_by_kubernetes.json
│ │ └── jvm_by_opentelementry.json
│ ├── Jenkins/
│ │ ├── collect/
│ │ │ └── jenkins/
│ │ │ └── jenkins.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Jolokia_Agent/
│ │ ├── collect/
│ │ │ └── jolokia_agent/
│ │ │ ├── activemq.toml
│ │ │ ├── bitbucket.toml
│ │ │ ├── cassandra.toml
│ │ │ ├── hadoop-hdfs.toml
│ │ │ ├── java.toml
│ │ │ ├── jboss.toml
│ │ │ ├── kafka-connect.toml
│ │ │ ├── kafka.toml
│ │ │ ├── tomcat.toml
│ │ │ ├── weblogic.toml
│ │ │ └── zookeeper.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Kafka/
│ │ ├── alerts/
│ │ │ ├── kafka_by_categraf.json
│ │ │ └── kafka_by_exporter.json
│ │ ├── collect/
│ │ │ └── kafka/
│ │ │ └── kafka.toml
│ │ ├── dashboards/
│ │ │ ├── kafka_by_categraf.json
│ │ │ └── kafka_by_exporter.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf-base.json
│ ├── Kubernetes/
│ │ ├── alerts/
│ │ │ ├── apiserver.json
│ │ │ ├── kube-controller-plane.json
│ │ │ ├── kubelet.json
│ │ │ ├── node-exporter.json
│ │ │ ├── prometheus-operator.json
│ │ │ └── prometheus.json
│ │ ├── dashboards/
│ │ │ ├── APIServer.json
│ │ │ ├── ControllerManager.json
│ │ │ ├── DeploymentContainer.json
│ │ │ ├── KubeStateMetrics.json
│ │ │ ├── KubeletMetrics.json
│ │ │ ├── Pod.json
│ │ │ ├── Scheduler.json
│ │ │ └── StatefulsetContainer.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ ├── metrics/
│ │ │ ├── k8s-node.json
│ │ │ └── k8s-pod.json
│ │ └── record-rules/
│ │ ├── kube-controller-plane.json
│ │ └── node-exporter.json
│ ├── Ldap/
│ │ ├── collect/
│ │ │ └── ldap/
│ │ │ └── ldap.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Linux/
│ │ ├── alerts/
│ │ │ ├── CommonAlertingRules-Categraf.json
│ │ │ ├── linux_by_categraf.json
│ │ │ ├── linux_by_exporter.json
│ │ │ ├── linux_by_telegraf.json
│ │ │ └── 常用中文告警规则-采集器Categraf.json
│ │ ├── collect/
│ │ │ ├── arp_packet/
│ │ │ │ └── arp_packet.toml
│ │ │ ├── kernel_vmstat/
│ │ │ │ └── kernel_vmstat.toml
│ │ │ ├── netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── ntp/
│ │ │ │ └── ntp.toml
│ │ │ └── processes/
│ │ │ └── processes.toml
│ │ ├── dashboards/
│ │ │ ├── categraf-detail.json
│ │ │ ├── categraf-overview.json
│ │ │ ├── categraf-processes.json
│ │ │ ├── categraf-table-ng.json
│ │ │ └── exporter-detail.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ ├── categraf-base.json
│ │ └── exporter-base.json
│ ├── Logstash/
│ │ ├── collect/
│ │ │ └── logstash/
│ │ │ └── logstash.toml
│ │ ├── dashboards/
│ │ │ └── logstash-dash.json
│ │ └── markdown/
│ │ └── README.md
│ ├── MinIO/
│ │ ├── alerts/
│ │ │ └── minio_by_categraf.json
│ │ ├── dashboards/
│ │ │ ├── minio_by_categraf.json
│ │ │ └── new-version.json
│ │ └── markdown/
│ │ └── README.md
│ ├── MongoDB/
│ │ ├── alerts/
│ │ │ └── mongo_by_exporter.json
│ │ ├── collect/
│ │ │ └── mongodb/
│ │ │ └── mongodb.toml
│ │ ├── dashboards/
│ │ │ └── mongo_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Mtail/
│ │ ├── collect/
│ │ │ └── mtail/
│ │ │ └── mtail.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── MySQL/
│ │ ├── alerts/
│ │ │ ├── mysql_by_categraf.json
│ │ │ └── mysql_by_exporter.json
│ │ ├── collect/
│ │ │ └── mysql/
│ │ │ └── mysql.toml
│ │ ├── dashboards/
│ │ │ ├── MySQL-by-address.json
│ │ │ ├── MySQL仪表盘-远端.json
│ │ │ ├── MySQL仪表盘.json
│ │ │ ├── mysql_by_categraf.json
│ │ │ ├── mysql_by_categraf_ident.json
│ │ │ ├── mysql_by_categraf_instance.json
│ │ │ └── mysql_by_exporter.json
│ │ ├── markdown/
│ │ │ ├── README.md
│ │ │ └── mysql.md
│ │ └── metrics/
│ │ └── categraf-base.json
│ ├── N9E/
│ │ ├── dashboards/
│ │ │ ├── n9e_server.json
│ │ │ ├── n9e_v6.json
│ │ │ └── n9e_v8.json
│ │ └── markdown/
│ │ └── README.md
│ ├── NFSClient/
│ │ ├── collect/
│ │ │ └── nfsclient/
│ │ │ └── nfsclient.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── NSQ/
│ │ ├── collect/
│ │ │ └── nsq/
│ │ │ └── nsq.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── NVIDIA/
│ │ ├── collect/
│ │ │ └── nvidia_smi/
│ │ │ └── nvidia_smi.toml
│ │ ├── dashboards/
│ │ │ └── nvidia-gpu-metrics-by-categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Net_Response/
│ │ ├── alerts/
│ │ │ └── net_response_by_categraf.json
│ │ ├── collect/
│ │ │ └── net_response/
│ │ │ └── net_response.toml
│ │ ├── dashboards/
│ │ │ ├── dashboard-by-ziv.json
│ │ │ └── net_response_by_categraf.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── Netstat_Filter/
│ │ ├── collect/
│ │ │ └── netstat_filter/
│ │ │ └── netstat_filter.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Nginx/
│ │ ├── collect/
│ │ │ ├── nginx/
│ │ │ │ └── nginx.toml
│ │ │ └── nginx_upstream_check/
│ │ │ └── nginx_upstream_check.toml
│ │ ├── dashboards/
│ │ │ ├── nginx_stub_status.json
│ │ │ ├── nginx_upstream_check.json
│ │ │ └── nginx_vts.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── Oracle/
│ │ ├── alerts/
│ │ │ └── oracle_alert.json
│ │ ├── collect/
│ │ │ └── oracle/
│ │ │ └── oracle.toml
│ │ ├── dashboards/
│ │ │ └── oracle_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── PHP/
│ │ ├── collect/
│ │ │ └── phpfpm/
│ │ │ └── phpfpm.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Ping/
│ │ ├── alerts/
│ │ │ └── ping_by_categraf.json
│ │ ├── collect/
│ │ │ └── ping/
│ │ │ └── ping.toml
│ │ ├── dashboards/
│ │ │ ├── ping_by_categraf_a.json
│ │ │ └── ping_by_categraf_b.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── PostgreSQL/
│ │ ├── alerts/
│ │ │ └── postgresql_by_categraf.json
│ │ ├── collect/
│ │ │ └── postgresql/
│ │ │ └── postgresql.toml
│ │ ├── dashboards/
│ │ │ └── postgresql_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Procstat/
│ │ ├── alerts/
│ │ │ └── categraf-procstat.json
│ │ ├── collect/
│ │ │ └── procstat/
│ │ │ └── procstat.toml
│ │ ├── dashboards/
│ │ │ └── categraf-procstat.json
│ │ ├── markdown/
│ │ │ └── readme.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── Prometheus/
│ │ ├── collect/
│ │ │ └── prometheus/
│ │ │ └── prometheus.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── RabbitMQ/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── collect/
│ │ │ └── rabbitmq/
│ │ │ └── rabbitmq.toml
│ │ ├── dashboards/
│ │ │ ├── rabbitmq_CN_v3.8_gt.json
│ │ │ ├── rabbitmq_by_categraf.json
│ │ │ ├── rabbitmq_v3.8_gt.json
│ │ │ └── rabbitmq_v3.8_lt.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Redis/
│ │ ├── alerts/
│ │ │ ├── redis_by_categraf.json
│ │ │ └── redis_by_exporter.json
│ │ ├── collect/
│ │ │ ├── redis/
│ │ │ │ └── redis.toml
│ │ │ └── redis_sentinel/
│ │ │ └── redis_sentinel.toml
│ │ ├── dashboards/
│ │ │ ├── FilterByAddress.json
│ │ │ ├── redis_by_categraf.json
│ │ │ └── redis_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SMART/
│ │ ├── collect/
│ │ │ └── smart/
│ │ │ └── smart.toml
│ │ ├── dashboards/
│ │ │ └── smart.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SNMP/
│ │ ├── collect/
│ │ │ └── snmp/
│ │ │ ├── Cisco.toml
│ │ │ ├── snmp.toml
│ │ │ └── snmp.toml.example
│ │ ├── dashboards/
│ │ │ ├── dashboards.json
│ │ │ ├── switch branch.json
│ │ │ └── switch main.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SQLServer/
│ │ ├── collect/
│ │ │ └── sqlserver/
│ │ │ └── sqlserver.toml
│ │ ├── dashboards/
│ │ │ └── sqlserver.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SpringBoot/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── dashboards/
│ │ │ ├── JVM(Actuator)withapplicationname.json
│ │ │ └── JVM.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Switch_Legacy/
│ │ ├── collect/
│ │ │ └── switch_legacy/
│ │ │ └── switch_legacy.toml
│ │ ├── dashboards/
│ │ │ └── dashboard.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Systemd/
│ │ ├── collect/
│ │ │ └── systemd/
│ │ │ └── systemd.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── TDEngine/
│ │ ├── dashboards/
│ │ │ └── tasokeeper3.x.json
│ │ └── markdown/
│ │ └── README.md
│ ├── TiDB/
│ │ ├── alerts/
│ │ │ └── tidb-alerts.json
│ │ └── dashboards/
│ │ └── tidb-dashboard.json
│ ├── Tomcat/
│ │ ├── collect/
│ │ │ └── tomcat/
│ │ │ └── tomcat.toml
│ │ ├── dashboards/
│ │ │ └── tomcat_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── VictoriaMetrics/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── dashboards/
│ │ │ ├── victoriametrics-cluster.json
│ │ │ └── victoriametrics-single.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Whois/
│ │ ├── collect/
│ │ │ └── whois/
│ │ │ └── whois.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Windows/
│ │ ├── alerts/
│ │ │ ├── windows_by_categraf.json
│ │ │ └── windows_by_exporter.json
│ │ ├── dashboards/
│ │ │ ├── windows_by_categraf.json
│ │ │ └── windows_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── XSKYApi/
│ │ ├── collect/
│ │ │ └── xskyapi/
│ │ │ └── xskyapi.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── ZooKeeper/
│ │ ├── alerts/
│ │ │ └── zookeeper_by_exporter.json
│ │ ├── collect/
│ │ │ └── zookeeper/
│ │ │ └── zookeeper.toml
│ │ ├── dashboards/
│ │ │ └── zookeeper_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── cAdvisor/
│ │ ├── collect/
│ │ │ └── cadvisor/
│ │ │ └── cadvisor.toml
│ │ ├── dashboards/
│ │ │ └── dashboard.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── exporter-base.json
│ └── vSphere/
│ ├── alerts/
│ │ └── alerts.json
│ ├── collect/
│ │ └── vsphere/
│ │ └── vsphere.toml
│ ├── dashboards/
│ │ ├── vmware_by_vsphere-monitor.json
│ │ └── vsphere.json
│ └── markdown/
│ └── README.md
├── memsto/
│ ├── alert_mute_cache.go
│ ├── alert_rule_cache.go
│ ├── alert_subscribe_cache.go
│ ├── busi_group_cache.go
│ ├── config_cache.go
│ ├── config_cval_cache.go
│ ├── datasource_cache.go
│ ├── drop_ident.go
│ ├── es_index_pattern.go
│ ├── event_processor_cache.go
│ ├── host_alert_rule_targets.go
│ ├── memsto.go
│ ├── message_template_cache.go
│ ├── notify_channel_cache.go
│ ├── notify_config.go
│ ├── notify_rule_cache.go
│ ├── recording_rule_cache.go
│ ├── stat.go
│ ├── target_cache.go
│ ├── task_tpl_cache.go
│ ├── user_cache.go
│ ├── user_group_cache.go
│ └── user_token_cache.go
├── models/
│ ├── alert_aggr_view.go
│ ├── alert_cur_event.go
│ ├── alert_his_event.go
│ ├── alert_mute.go
│ ├── alert_rule.go
│ ├── alert_subscribe.go
│ ├── alerting_engine.go
│ ├── anomaly_point.go
│ ├── board.go
│ ├── board_busi.go
│ ├── board_payload.go
│ ├── builtin_cate.go
│ ├── builtin_component.go
│ ├── builtin_metrics.go
│ ├── builtin_metrics_filter.go
│ ├── builtin_payload.go
│ ├── busi_group.go
│ ├── busi_group_member.go
│ ├── chart.go
│ ├── chart_group.go
│ ├── chart_share.go
│ ├── common.go
│ ├── configs.go
│ ├── dash_annotation.go
│ ├── dashboard.go
│ ├── datasource.go
│ ├── embedded_product.go
│ ├── es_index_pattern.go
│ ├── event_pipeline.go
│ ├── event_pipeline_execution.go
│ ├── event_processor.go
│ ├── host_meta.go
│ ├── message_tpl.go
│ ├── metric_view.go
│ ├── migrate/
│ │ ├── migrate.go
│ │ ├── migrate_es_index_pattern.go
│ │ └── migrate_test.go
│ ├── notification_record.go
│ ├── notify_channel.go
│ ├── notify_channel_test.go
│ ├── notify_config.go
│ ├── notify_rule.go
│ ├── notify_tpl.go
│ ├── prom_alert_rule.go
│ ├── prom_alert_rule_test.go
│ ├── recording_rule.go
│ ├── role.go
│ ├── role_operation.go
│ ├── saved_view.go
│ ├── source_token.go
│ ├── sso_config.go
│ ├── target.go
│ ├── target_busi_group.go
│ ├── task_record.go
│ ├── task_tpl.go
│ ├── ts.go
│ ├── user.go
│ ├── user_group.go
│ ├── user_group_member.go
│ ├── user_token.go
│ └── workflow.go
├── pkg/
│ ├── aop/
│ │ ├── log.go
│ │ └── rec.go
│ ├── cas/
│ │ └── cas.go
│ ├── cfg/
│ │ ├── cfg.go
│ │ └── scan.go
│ ├── choice/
│ │ └── choice.go
│ ├── cmdx/
│ │ ├── cmd_notwindows.go
│ │ ├── cmd_windows.go
│ │ └── cmdx.go
│ ├── ctx/
│ │ └── ctx.go
│ ├── dingtalk/
│ │ ├── dingtalk.go
│ │ └── user/
│ │ └── client.go
│ ├── fasttime/
│ │ └── fasttime.go
│ ├── feishu/
│ │ └── feishu.go
│ ├── flashduty/
│ │ ├── post.go
│ │ ├── sync_user.go
│ │ ├── sync_user_group.go
│ │ └── sync_user_test.go
│ ├── ginx/
│ │ ├── auth.go
│ │ ├── bytesconv.go
│ │ ├── errorx.go
│ │ ├── funcs.go
│ │ ├── param.go
│ │ └── render.go
│ ├── hash/
│ │ ├── hash.go
│ │ ├── hash_fnv.go
│ │ └── hash_md5.go
│ ├── httpx/
│ │ └── httpx.go
│ ├── i18nx/
│ │ ├── i18n.go
│ │ └── var.go
│ ├── ibex/
│ │ └── ibex.go
│ ├── ldapx/
│ │ ├── ldapx.go
│ │ └── user_sync.go
│ ├── loggrep/
│ │ └── loggrep.go
│ ├── logx/
│ │ └── logx.go
│ ├── macros/
│ │ └── macros.go
│ ├── oauth2x/
│ │ └── oauth2x.go
│ ├── oidcx/
│ │ └── oidc.go
│ ├── ormx/
│ │ ├── database_init.go
│ │ ├── database_init_test.go
│ │ ├── ormx.go
│ │ └── types.go
│ ├── osx/
│ │ └── osx.go
│ ├── parser/
│ │ ├── calc.go
│ │ └── calc_test.go
│ ├── poster/
│ │ ├── post.go
│ │ └── post_test.go
│ ├── prom/
│ │ ├── client_option.go
│ │ ├── conv.go
│ │ ├── conv_test.go
│ │ ├── reader.go
│ │ └── writer.go
│ ├── promql/
│ │ ├── parser.go
│ │ ├── perser_test.go
│ │ └── promql.go
│ ├── secu/
│ │ ├── aes.go
│ │ └── rsa.go
│ ├── slice/
│ │ └── contains.go
│ ├── strx/
│ │ └── verify.go
│ ├── tlsx/
│ │ ├── common.go
│ │ └── config.go
│ ├── tplx/
│ │ ├── conv.go
│ │ ├── fns.go
│ │ ├── tpl_test.go
│ │ └── tplx.go
│ ├── unit/
│ │ ├── unit_convert.go
│ │ └── unit_convert_test.go
│ └── version/
│ └── version.go
├── prom/
│ ├── client.go
│ ├── option.go
│ └── reader.go
├── pushgw/
│ ├── idents/
│ │ └── idents.go
│ ├── kafka/
│ │ └── producer.go
│ ├── pconf/
│ │ └── conf.go
│ ├── pstat/
│ │ └── pstat.go
│ ├── pushgw.go
│ ├── router/
│ │ ├── fns.go
│ │ ├── router.go
│ │ ├── router_datadog.go
│ │ ├── router_datadog_easyjson.go
│ │ ├── router_heartbeat.go
│ │ ├── router_openfalcon.go
│ │ ├── router_openfalcon_easyjson.go
│ │ ├── router_opentsdb.go
│ │ ├── router_opentsdb_easyjson.go
│ │ ├── router_proxy_remotewrite.go
│ │ ├── router_remotewrite.go
│ │ ├── router_target.go
│ │ └── vars.go
│ └── writer/
│ ├── kafka_writer.go
│ ├── queue.go
│ ├── relabel.go
│ ├── relabel_test.go
│ └── writer.go
└── storage/
├── redis.go
├── redis_test.go
└── storage.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.css linguist-language=go
*.less linguist-language=go
*.js linguist-language=go
*.tsx linguist-language=go
*.html linguist-language=go
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Nightingale docs
url: https://n9e.github.io/
about: You may want to read through the document before asking questions.
================================================
FILE: .github/ISSUE_TEMPLATE/enhancement.md
================================================
---
name: Enhancement Request
about: Suggest an enhancement to the nightingale project
labels: kind/feature
---
<!-- Please only use this template for submitting enhancement requests -->
**What would you like to be added**:
**Why is this needed**:
================================================
FILE: .github/ISSUE_TEMPLATE/question.yml
================================================
name: Bug Report & Usage Question
description: Reporting a bug or asking a question about how to use Nightingale
labels: []
body:
- type: markdown
attributes:
value: |
The more detailed the form is filled in, the easier the problem will be solved.
提供的信息越详细,问题解决的可能性就越大。另外, 提问之前请先搜索历史 issue (包括 close 的), 以免重复提问。
- type: textarea
id: question
attributes:
label: Question and Steps to reproduce
description: Describe your question and steps to reproduce the bug. 描述问题以及复现步骤
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant logs and configurations
description: Relevant logs and configurations. 报错日志([查看方法](https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v6/faq/how-to-check-logs/))以及各个相关组件的配置信息
render: text
validations:
required: true
- type: textarea
id: system-info
attributes:
label: Version
description: Include nightingale version, operating system, and other relevant details. 请告知夜莺的版本、操作系统的版本、CPU架构等信息
validations:
required: true
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
**What type of PR is this?**
**What this PR does / why we need it**:
<!--
"Nice to have" "You need it" is not a good reason. :)
-->
**Which issue(s) this PR fixes**:
<!--
Usage: `Fixes #<issue number>`, or "Fixes (paste link of issue)"
-->
Fixes #
**Special notes for your reviewer**:
================================================
FILE: .github/workflows/issue-translator.yml
================================================
name: 'Issue Translator'
on:
issues:
types: [opened]
jobs:
translate:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Translate Issues
uses: usthe/issues-translate-action@v2.7
with:
# 是否翻译 issue 标题
IS_MODIFY_TITLE: true
# GitHub Token
BOT_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 自定义翻译标注(可选)
# CUSTOM_BOT_NOTE: "Translation by bot"
================================================
FILE: .github/workflows/n9e.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
env:
GO_VERSION: 1.23
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Go Environment
uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
- uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
distribution: goreleaser
version: '~> v1'
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
*.exe
*.exe~
*.dll
*.dylib
*.test
*.out
*.prof
*.log
*.o
*.a
*.so
*.db
*.sw[po]
*.tar.gz
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
_obj
_test
/log*
/bin
/out
/build
/dist
/etc/*.local.yml
/etc/*.local.conf
/etc/rsa/*
/etc/plugins/*.local.yml
/etc/script/rules.yaml
/etc/script/alert-rules.json
/etc/script/record-rules.json
/data*
/tarball
/run
/vendor
/tmp
/pub
/n9e
/docker/pub
/docker/n9e
/docker/compose-bridge/mysqldata
/docker/compose-host-network/mysqldata
/docker/compose-host-network-metric-log/mysqldata
/docker/compose-host-network-metric-log/n9e-logs
/docker/compose-postgres/pgdata
/etc.local*
/front/statik/statik.go
/docker/compose-bridge/etc-nightingale/rsa/
.alerts
.idea
.index
.vscode
.issue
.issue/*
.cursor
.claude
.DS_Store
.cache-loader
.payload
queries.active
/n9e-*
n9e.sql
!/datasource
.env.json
================================================
FILE: .goreleaser.yaml
================================================
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
- go install github.com/rakyll/statik
snapshot:
name_template: '{{ .Tag }}'
checksum:
name_template: 'checksums.txt'
changelog:
skip: true
builds:
- id: build
hooks:
pre:
- cmd: sh -x ./fe.sh
output: true
main: ./cmd/center/
binary: n9e
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X github.com/ccfos/nightingale/v6/pkg/version.Version={{ .Tag }}-{{.Commit}}
- id: build-cli
main: ./cmd/cli/
binary: n9e-cli
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X github.com/ccfos/nightingale/v6/pkg/version.Version={{ .Tag }}-{{.Commit}}
- id: build-edge
main: ./cmd/edge/
binary: n9e-edge
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X github.com/ccfos/nightingale/v6/pkg/version.Version={{ .Tag }}-{{.Commit}}
archives:
- id: n9e
builds:
- build
- build-cli
- build-edge
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: "n9e-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
wrap_in_directory: false
files:
- docker/*
- etc/*
- integrations/*
- cli/*
- n9e.sql
release:
github:
owner: ccfos
name: nightingale
name_template: "v{{ .Version }}"
dockers:
- image_templates:
- flashcatcloud/nightingale:{{ .Version }}-amd64
goos: linux
goarch: amd64
ids:
- build
dockerfile: docker/Dockerfile.goreleaser
extra_files:
- etc
- integrations
use: buildx
build_flag_templates:
- "--platform=linux/amd64"
- image_templates:
- flashcatcloud/nightingale:{{ .Version }}-arm64v8
goos: linux
goarch: arm64
ids:
- build
dockerfile: docker/Dockerfile.goreleaser.arm64
extra_files:
- etc
- integrations
use: buildx
build_flag_templates:
- "--platform=linux/arm64/v8"
docker_manifests:
- name_template: flashcatcloud/nightingale:{{ .Version }}
image_templates:
- flashcatcloud/nightingale:{{ .Version }}-amd64
- flashcatcloud/nightingale:{{ .Version }}-arm64v8
- name_template: flashcatcloud/nightingale:latest
image_templates:
- flashcatcloud/nightingale:{{ .Version }}-amd64
- flashcatcloud/nightingale:{{ .Version }}-arm64v8
================================================
FILE: .typos.toml
================================================
# Configuration for typos tool
[files]
extend-exclude = [
# Ignore auto-generated easyjson files
"*_easyjson.go",
# Ignore binary files
"*.gz",
"*.tar",
"n9e",
"n9e-*"
]
[default.extend-identifiers]
# Didi is a company name (DiDi), not a typo
Didi = "Didi"
# datas is intentionally used as plural of data (slice variable)
datas = "datas"
# pendings is intentionally used as plural
pendings = "pendings"
pendingsUseByRecover = "pendingsUseByRecover"
pendingsUseByRecoverMap = "pendingsUseByRecoverMap"
# typs is intentionally used as shorthand for types (parameter name)
typs = "typs"
[default.extend-words]
# Some false positives
ba = "ba"
# Specific corrections for ambiguous typos
contigious = "contiguous"
onw = "own"
componet = "component"
Patten = "Pattern"
Requets = "Requests"
Mis = "Miss"
exporer = "exporter"
soruce = "source"
verison = "version"
Configations = "Configurations"
emmited = "emitted"
Utlization = "Utilization"
serie = "series"
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright CCF ODC.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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.
================================================
FILE: Makefile
================================================
.PHONY: prebuild build
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)"
all: prebuild build
prebuild:
echo "begin download and embed the front-end file..."
sh fe.sh
echo "front-end file download and embedding completed."
build:
go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e ./cmd/center/main.go
build-edge:
go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-edge ./cmd/edge/
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
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
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
run:
nohup ./n9e > n9e.log 2>&1 &
run-alert:
nohup ./n9e-alert > n9e-alert.log 2>&1 &
run-pushgw:
nohup ./n9e-pushgw > n9e-pushgw.log 2>&1 &
release:
goreleaser --skip-validate --skip-publish --snapshot
================================================
FILE: README.md
================================================
<p align="center">
<a href="https://github.com/ccfos/nightingale">
<img src="doc/img/Nightingale_L_V.png" alt="nightingale - cloud native monitoring" width="100" /></a>
</p>
<p align="center">
<b>Open-Source Alerting Expert</b>
</p>
<p align="center">
<a href="https://flashcat.cloud/docs/">
<img alt="Docs" src="https://img.shields.io/badge/docs-get%20started-brightgreen"/></a>
<a href="https://hub.docker.com/u/flashcatcloud">
<img alt="Docker pulls" src="https://img.shields.io/docker/pulls/flashcatcloud/nightingale"/></a>
<a href="https://github.com/ccfos/nightingale/graphs/contributors">
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors-anon/ccfos/nightingale"/></a>
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ccfos/nightingale">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/ccfos/nightingale">
<br/><img alt="GitHub Repo issues" src="https://img.shields.io/github/issues/ccfos/nightingale">
<img alt="GitHub Repo issues closed" src="https://img.shields.io/github/issues-closed/ccfos/nightingale">
<img alt="GitHub latest release" src="https://img.shields.io/github/v/release/ccfos/nightingale"/>
<img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue"/>
<a href="https://n9e-talk.slack.com/">
<img alt="GitHub contributors" src="https://img.shields.io/badge/join%20slack-%23n9e-brightgreen.svg"/></a>
</p>
[English](./README.md) | [中文](./README_zh.md)
## 🎯 What is Nightingale
Nightingale is an open-source monitoring project that focuses on alerting. Similar to Grafana, Nightingale also connects with various existing data sources. However, while Grafana emphasizes visualization, Nightingale places greater emphasis on the alerting engine, as well as the processing and distribution of alarms.
> 💡 Nightingale has now officially launched the [MCP-Server](https://github.com/n9e/n9e-mcp-server/). This MCP Server enables AI assistants to interact with the Nightingale API using natural language, facilitating alert management, monitoring, and observability tasks.
>
> The Nightingale project was initially developed and open-sourced by DiDi.inc. On May 11, 2022, it was donated to the Open Source Development Committee of the China Computer Federation (CCF ODTC).

## 💡 How Nightingale Works
Many users have already collected metrics and log data. In this case, you can connect your storage repositories (such as VictoriaMetrics, ElasticSearch, etc.) as data sources in Nightingale. This allows you to configure alerting rules and notification rules within Nightingale, enabling the generation and distribution of alarms.

Nightingale itself does not provide monitoring data collection capabilities. We recommend using [Categraf](https://github.com/flashcatcloud/categraf) as the collector, which integrates seamlessly with Nightingale.
[Categraf](https://github.com/flashcatcloud/categraf) can collect monitoring data from operating systems, network devices, various middleware, and databases. It pushes this data to Nightingale via the `Prometheus Remote Write` protocol. Nightingale then stores the monitoring data in a time-series database (such as Prometheus, VictoriaMetrics, etc.) and provides alerting and visualization capabilities.
For certain edge data centers with poor network connectivity to the central Nightingale server, we offer a distributed deployment mode for the alerting engine. In this mode, even if the network is disconnected, the alerting functionality remains unaffected.

> In the above diagram, Data Center A has a good network with the central data center, so it uses the Nightingale process in the central data center as the alerting engine. Data Center B has a poor network with the central data center, so it deploys `n9e-edge` as the alerting engine to handle alerting for its own data sources.
## 🔕 Alert Noise Reduction, Escalation, and Collaboration
Nightingale focuses on being an alerting engine, responsible for generating alarms and flexibly distributing them based on rules. It supports 20 built-in notification medias (such as phone calls, SMS, email, DingTalk, Slack, etc.).
If you have more advanced requirements, such as:
- Want to consolidate events from multiple monitoring systems into one platform for unified noise reduction, response handling, and data analysis.
- Want to support personnel scheduling, practice on-call culture, and support alert escalation (to avoid missing alerts) and collaborative handling.
Then Nightingale is not suitable. It is recommended that you choose on-call products such as PagerDuty and FlashDuty. These products are simple and easy to use.
## 🗨️ Communication Channels
- **Report Bugs:** It is highly recommended to submit issues via the [Nightingale GitHub Issue tracker](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=kind%2Fbug&projects=&template=bug_report.yml).
- **Documentation:** For more information, we recommend thoroughly browsing the [Nightingale Documentation Site](https://n9e.github.io/).
## 🔑 Key Features

- Nightingale supports alerting rules, mute rules, subscription rules, and notification rules. It natively supports 20 types of notification media and allows customization of message templates.
- It supports event pipelines for Pipeline processing of alarms, facilitating automated integration with in-house systems. For example, it can append metadata to alarms or perform relabeling on events.
- It introduces the concept of business groups and a permission system to manage various rules in a categorized manner.
- Many databases and middleware come with built-in alert rules that can be directly imported and used. It also supports direct import of Prometheus alerting rules.
- It supports alerting self-healing, which automatically triggers a script to execute predefined logic after an alarm is generated—such as cleaning up disk space or capturing the current system state.

- Nightingale archives historical alarms and supports multi-dimensional query and statistics.
- It supports flexible aggregation grouping, allowing a clear view of the distribution of alarms across the company.

- Nightingale has built-in metric descriptions, dashboards, and alerting rules for common operating systems, middleware, and databases, which are contributed by the community with varying quality.
- It directly receives data via multiple protocols such as Remote Write, OpenTSDB, Datadog, and Falcon, integrates with various Agents.
- It supports data sources like Prometheus, ElasticSearch, Loki, ClickHouse, MySQL, Postgres, allowing alerting based on data from these sources.
- Nightingale can be easily embedded into internal enterprise systems (e.g. Grafana, CMDB), and even supports configuring menu visibility for these embedded systems.

- Nightingale supports dashboard functionality, including common chart types, and comes with pre-built dashboards. The image above is a screenshot of one of these dashboards.
- If you are already accustomed to Grafana, it is recommended to continue using Grafana for visualization, as Grafana has deeper expertise in this area.
- For machine-related monitoring data collected by Categraf, it is advisable to use Nightingale's built-in dashboards for viewing. This is because Categraf's metric naming follows Telegraf's convention, which differs from that of Node Exporter.
- Due to Nightingale's concept of business groups (where machines can belong to different groups), there may be scenarios where you only want to view machines within the current business group on the dashboard. Thus, Nightingale's dashboards can be linked with business groups for interactive filtering.
## 🌟 Stargazers over time
[](https://star-history.com/#ccfos/nightingale&Date)
## 🔥 Users

## 🤝 Community Co-Building
- ❇️ Please read the [Nightingale Open Source Project and Community Governance Draft](./doc/community-governance.md). We sincerely welcome every user, developer, company, and organization to use Nightingale, actively report bugs, submit feature requests, share best practices, and help build a professional and active open-source community.
- ❤️ Nightingale Contributors
<a href="https://github.com/ccfos/nightingale/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ccfos/nightingale" />
</a>
## 📜 License
- [Apache License V2.0](https://github.com/ccfos/nightingale/blob/main/LICENSE)
================================================
FILE: README_zh.md
================================================
<p align="center">
<a href="https://github.com/ccfos/nightingale">
<img src="doc/img/Nightingale_L_V.png" alt="nightingale - cloud native monitoring" width="100" /></a>
</p>
<p align="center">
<b>开源监控告警管理专家</b>
</p>
<p align="center">
<a href="https://flashcat.cloud/docs/">
<img alt="Docs" src="https://img.shields.io/badge/docs-get%20started-brightgreen"/></a>
<a href="https://hub.docker.com/u/flashcatcloud">
<img alt="Docker pulls" src="https://img.shields.io/docker/pulls/flashcatcloud/nightingale"/></a>
<a href="https://github.com/ccfos/nightingale/graphs/contributors">
<img alt="GitHub contributors" src="https://img.shields.io/github/contributors-anon/ccfos/nightingale"/></a>
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ccfos/nightingale">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/ccfos/nightingale">
<br/><img alt="GitHub Repo issues" src="https://img.shields.io/github/issues/ccfos/nightingale">
<img alt="GitHub Repo issues closed" src="https://img.shields.io/github/issues-closed/ccfos/nightingale">
<img alt="GitHub latest release" src="https://img.shields.io/github/v/release/ccfos/nightingale"/>
<img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue"/>
<a href="https://n9e-talk.slack.com/">
<img alt="GitHub contributors" src="https://img.shields.io/badge/join%20slack-%23n9e-brightgreen.svg"/></a>
</p>
[English](./README.md) | [中文](./README_zh.md)
## 夜莺是什么
夜莺 Nightingale 是一款开源云原生监控告警工具,是中国计算机学会接受捐赠并托管的第一个开源项目,在 GitHub 上有超过 12000 颗星,广受关注和使用。夜莺的统一告警引擎,可以对接 Prometheus、Elasticsearch、ClickHouse、Loki、MySQL 等多种数据源,提供全面的告警判定、丰富的事件处理和灵活的告警分发及通知能力。
夜莺侧重于监控告警,类似于 Grafana 的数据源集成方式,夜莺也是对接多种既有的数据源,不过 Grafana 侧重于可视化,夜莺则是侧重于告警引擎、告警事件的处理和分发。
> - 💡夜莺正式推出了 [MCP-Server](https://github.com/n9e/n9e-mcp-server/),此 MCP Server 允许 AI 助手通过自然语言与夜莺 API 交互,实现告警管理、监控和可观测性任务。
> - 夜莺监控项目,最初由滴滴开发和开源,并于 2022 年 5 月 11 日,捐赠予中国计算机学会开源发展技术委员会(CCF ODTC),为 CCF ODTC 成立后接受捐赠的第一个开源项目。

## 夜莺的工作逻辑
很多用户已经自行采集了指标、日志数据,此时就把存储库(VictoriaMetrics、ElasticSearch等)作为数据源接入夜莺,即可在夜莺里配置告警规则、通知规则,完成告警事件的生成和派发。

夜莺项目本身不提供监控数据采集能力。推荐您使用 [Categraf](https://github.com/flashcatcloud/categraf) 作为采集器,可以和夜莺丝滑对接。
[Categraf](https://github.com/flashcatcloud/categraf) 可以采集操作系统、网络设备、各类中间件、数据库的监控数据,通过 Remote Write 协议推送给夜莺,夜莺把监控数据转存到时序库(如 Prometheus、VictoriaMetrics 等),并提供告警和可视化能力。
对于个别边缘机房,如果和中心夜莺服务端网络链路不好,希望提升告警可用性,夜莺也提供边缘机房告警引擎下沉部署模式,这个模式下,即便边缘和中心端网络割裂,告警功能也不受影响。

> 上图中,机房A和中心机房的网络链路很好,所以直接由中心端的夜莺进程做告警引擎,机房B和中心机房的网络链路不好,所以在机房B部署了 `n9e-edge` 做告警引擎,对机房B的数据源做告警判定。
## 告警降噪、升级、协同
夜莺的侧重点是做告警引擎,即负责产生告警事件,并根据规则做灵活派发,内置支持 20 种通知媒介(电话、短信、邮件、钉钉、飞书、企微、Slack 等)。
如果您有更高级的需求,比如:
- 想要把公司的多套监控系统产生的事件聚拢到一个平台,统一做收敛降噪、响应处理、数据分析
- 想要支持人员的排班,践行 On-call 文化,想要支持告警认领、升级(避免遗漏)、协同处理
那夜莺是不合适的,推荐您选用 [FlashDuty](https://flashcat.cloud/product/flashcat-duty/) 这样的 On-call 产品,产品简单易用,也有免费套餐。
## 相关资料 & 交流渠道
- 📚 [夜莺介绍PPT](https://mp.weixin.qq.com/s/Mkwx_46xrltSq8NLqAIYow) 对您了解夜莺各项关键特性会有帮助(PPT链接在文末)
- 👉 [文档中心](https://flashcat.cloud/docs/) 为了更快的访问速度,站点托管在 [FlashcatCloud](https://flashcat.cloud)
- ❤️ [报告 Bug](https://github.com/ccfos/nightingale/issues/new?assignees=&labels=&projects=&template=question.yml) 写清楚问题描述、复现步骤、截图等信息,更容易得到答案
- 💡 前后端代码分离,前端代码仓库:[https://github.com/n9e/fe](https://github.com/n9e/fe)
- 🎯 关注[这个公众号](https://gitlink.org.cn/UlricQin)了解更多夜莺动态和知识
- 🌟 加我微信:`picobyte`(我已关闭好友验证)拉入微信群,备注:`夜莺互助群`,如果已经把夜莺上到生产环境,可联系我拉入资深监控用户群
## 关键特性简介

- 夜莺支持告警规则、屏蔽规则、订阅规则、通知规则,内置支持 20 种通知媒介,支持消息模板自定义
- 支持事件管道,对告警事件做 Pipeline 处理,方便和自有系统做自动化整合,比如给告警事件附加一些元信息,对事件做 relabel
- 支持业务组概念,引入权限体系,分门别类管理各类规则
- 很多数据库、中间件内置了告警规则,可以直接导入使用,也可以直接导入 Prometheus 的告警规则
- 支持告警自愈,即告警之后自动触发一个脚本执行一些预定义的逻辑,比如清理一下磁盘、抓一下现场等

- 夜莺存档了历史告警事件,支持多维度的查询和统计
- 支持灵活的聚合分组,一目了然看到公司的告警事件分布情况

- 夜莺内置常用操作系统、中间件、数据库的的指标说明、仪表盘、告警规则,不过都是社区贡献的,整体也是参差不齐
- 夜莺直接接收 Remote Write、OpenTSDB、Datadog、Falcon 等多种协议的数据,故而可以和各类 Agent 对接
- 夜莺支持 Prometheus、ElasticSearch、Loki、TDEngine 等多种数据源,可以对其中的数据做告警
- 夜莺可以很方便内嵌企业内部系统,比如 Grafana、CMDB 等,甚至可以配置这些内嵌系统的菜单可见性

- 夜莺支持仪表盘功能,支持常见的图表类型,也内置了一些仪表盘,上图是其中一个仪表盘的截图。
- 如果你已经习惯了 Grafana,建议仍然使用 Grafana 看图。Grafana 在看图方面道行更深。
- 机器相关的监控数据,如果是 Categraf 采集的,建议使用夜莺自带的仪表盘查看,因为 Categraf 的指标命名 Follow 的是 Telegraf 的命名方式,和 Node Exporter 不同
- 因为夜莺有个业务组的概念,机器可以归属不同的业务组,有时在仪表盘里只想查看当前所属业务组的机器,所以夜莺的仪表盘可以和业务组联动
## 广受关注
[](https://star-history.com/#ccfos/nightingale&Date)
## 感谢众多企业的信赖

## 社区共建
- ❇️ 请阅读浏览[夜莺开源项目和社区治理架构草案](./doc/community-governance.md),真诚欢迎每一位用户、开发者、公司以及组织,使用夜莺监控、积极反馈 Bug、提交功能需求、分享最佳实践,共建专业、活跃的夜莺开源社区。
- ❤️ 夜莺贡献者
<a href="https://github.com/ccfos/nightingale/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ccfos/nightingale" />
</a>
## License
- [Apache License V2.0](https://github.com/ccfos/nightingale/blob/main/LICENSE)
================================================
FILE: alert/aconf/conf.go
================================================
package aconf
import (
"path"
)
type Alert struct {
Disable bool
EngineDelay int64
Heartbeat HeartbeatConfig
Alerting Alerting
}
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
EngineName string
}
type Alerting struct {
Timeout int64
TemplatesDir string
NotifyConcurrency int
WebhookBatchSend bool
GlobalWebhook GlobalWebhook
}
type GlobalWebhook struct {
Enable bool
Url string
BasicAuthUser string
BasicAuthPass string
Timeout int
Headers []string
SkipVerify bool
}
type CallPlugin struct {
Enable bool
PluginPath string
Caller string
}
type RedisPub struct {
Enable bool
ChannelPrefix string
ChannelKey string
}
func (a *Alert) PreCheck(configDir string) {
if a.Alerting.TemplatesDir == "" {
a.Alerting.TemplatesDir = path.Join(configDir, "template")
}
if a.Alerting.NotifyConcurrency == 0 {
a.Alerting.NotifyConcurrency = 10
}
if a.Heartbeat.Interval == 0 {
a.Heartbeat.Interval = 1000
}
if a.EngineDelay == 0 {
a.EngineDelay = 30
}
}
================================================
FILE: alert/alert.go
================================================
package alert
import (
"context"
"fmt"
"github.com/ccfos/nightingale/v6/dscache"
"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/dumper"
"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/pkg/macros"
"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"
"github.com/flashcatcloud/ibex/src/cmd/ibex"
)
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
}
ctx := ctx.NewContext(context.Background(), nil, false, config.CenterApi)
var redis storage.Redis
redis, err = storage.NewRedis(config.Redis)
if err != nil {
return nil, err
}
syncStats := memsto.NewSyncStats()
alertStats := astats.NewSyncStats()
configCache := memsto.NewConfigCache(ctx, syncStats, nil, "")
targetCache := memsto.NewTargetCache(ctx, syncStats, redis)
busiGroupCache := memsto.NewBusiGroupCache(ctx, syncStats)
alertMuteCache := memsto.NewAlertMuteCache(ctx, syncStats)
alertRuleCache := memsto.NewAlertRuleCache(ctx, syncStats)
notifyConfigCache := memsto.NewNotifyConfigCache(ctx, configCache)
dsCache := memsto.NewDatasourceCache(ctx, syncStats)
userCache := memsto.NewUserCache(ctx, syncStats)
userGroupCache := memsto.NewUserGroupCache(ctx, syncStats)
taskTplsCache := memsto.NewTaskTplCache(ctx)
configCvalCache := memsto.NewCvalCache(ctx, syncStats)
notifyRuleCache := memsto.NewNotifyRuleCache(ctx, syncStats)
notifyChannelCache := memsto.NewNotifyChannelCache(ctx, syncStats)
messageTemplateCache := memsto.NewMessageTemplateCache(ctx, syncStats)
promClients := prom.NewPromClient(ctx)
dispatch.InitRegisterQueryFunc(promClients)
externalProcessors := process.NewExternalProcessors()
macros.RegisterMacro(macros.MacroInVain)
dscache.Init(ctx, false)
Start(config.Alert, config.Pushgw, syncStats, alertStats, externalProcessors, targetCache, busiGroupCache, alertMuteCache, alertRuleCache, notifyConfigCache, taskTplsCache, dsCache, ctx, promClients, userCache, userGroupCache, notifyRuleCache, notifyChannelCache, messageTemplateCache, configCvalCache)
r := httpx.GinEngine(config.Global.RunMode, config.HTTP,
configCvalCache.PrintBodyPaths, configCvalCache.PrintAccessLog)
rt := router.New(config.HTTP, config.Alert, alertMuteCache, targetCache, busiGroupCache, alertStats, ctx, externalProcessors, config.Log.Dir)
if config.Ibex.Enable {
ibex.ServerStart(false, nil, redis, config.HTTP.APIForService.BasicAuth, config.Alert.Heartbeat, &config.CenterApi, r, nil, config.Ibex, config.HTTP.Port)
}
rt.Config(r)
dumper.ConfigRouter(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, notifyConfigCache *memsto.NotifyConfigCacheType, taskTplsCache *memsto.TaskTplCache, datasourceCache *memsto.DatasourceCacheType, ctx *ctx.Context,
promClients *prom.PromClientMap, userCache *memsto.UserCacheType, userGroupCache *memsto.UserGroupCacheType, notifyRuleCache *memsto.NotifyRuleCacheType, notifyChannelCache *memsto.NotifyChannelCacheType, messageTemplateCache *memsto.MessageTemplateCacheType, configCvalCache *memsto.CvalCache) {
alertSubscribeCache := memsto.NewAlertSubscribeCache(ctx, syncStats)
recordingRuleCache := memsto.NewRecordingRuleCache(ctx, syncStats)
targetsOfAlertRulesCache := memsto.NewTargetOfAlertRuleCache(ctx, alertc.Heartbeat.EngineName, syncStats)
go models.InitNotifyConfig(ctx, alertc.Alerting.TemplatesDir)
go models.InitNotifyChannel(ctx)
go models.InitMessageTemplate(ctx)
naming := naming.NewNaming(ctx, alertc.Heartbeat, alertStats)
writers := writer.NewWriters(pushgwc)
record.NewScheduler(alertc, recordingRuleCache, promClients, writers, alertStats, datasourceCache)
eval.NewScheduler(alertc, externalProcessors, alertRuleCache, targetCache, targetsOfAlertRulesCache,
busiGroupCache, alertMuteCache, datasourceCache, promClients, naming, ctx, alertStats)
eventProcessorCache := memsto.NewEventProcessorCache(ctx, syncStats)
sender.InitStaticGlobalWebhook(alertc.Alerting.GlobalWebhook)
dp := dispatch.NewDispatch(alertRuleCache, userCache, userGroupCache, alertSubscribeCache, targetCache, notifyConfigCache, taskTplsCache, notifyRuleCache, notifyChannelCache, messageTemplateCache, eventProcessorCache, configCvalCache, alertc.Alerting, ctx, alertStats)
consumer := dispatch.NewConsumer(alertc.Alerting, ctx, dp, promClients, alertMuteCache)
notifyRecordConsumer := sender.NewNotifyRecordConsumer(ctx)
go dp.ReloadTpls()
go consumer.LoopConsume()
go notifyRecordConsumer.LoopConsume()
go queue.ReportQueueSize(alertStats)
go sender.ReportNotifyRecordQueueSize(alertStats)
go sender.InitEmailSender(ctx, notifyConfigCache)
}
================================================
FILE: alert/astats/stats.go
================================================
package astats
import (
"github.com/prometheus/client_golang/prometheus"
)
const (
namespace = "n9e"
subsystem = "alert"
)
type Stats struct {
AlertNotifyTotal *prometheus.CounterVec
AlertNotifyErrorTotal *prometheus.CounterVec
CounterAlertsTotal *prometheus.CounterVec
GaugeAlertQueueSize prometheus.Gauge
CounterRuleEval *prometheus.CounterVec
CounterQueryDataErrorTotal *prometheus.CounterVec
CounterQueryDataTotal *prometheus.CounterVec
CounterVarFillingQuery *prometheus.CounterVec
CounterRecordEval *prometheus.CounterVec
CounterRecordEvalErrorTotal *prometheus.CounterVec
CounterMuteTotal *prometheus.CounterVec
CounterRuleEvalErrorTotal *prometheus.CounterVec
CounterHeartbeatErrorTotal *prometheus.CounterVec
CounterSubEventTotal *prometheus.CounterVec
GaugeQuerySeriesCount *prometheus.GaugeVec
GaugeRuleEvalDuration *prometheus.GaugeVec
GaugeNotifyRecordQueueSize prometheus.Gauge
}
func NewSyncStats() *Stats {
CounterRuleEval := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "rule_eval_total",
Help: "Number of rule eval.",
}, []string{})
CounterRuleEvalErrorTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "rule_eval_error_total",
Help: "Number of rule eval error.",
}, []string{"datasource", "stage", "busi_group", "rule_id"})
CounterQueryDataErrorTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "query_data_error_total",
Help: "Number of rule eval query data error.",
}, []string{"datasource"})
CounterQueryDataTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "query_data_total",
Help: "Number of rule eval query data.",
}, []string{"datasource", "rule_id"})
CounterRecordEval := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "record_eval_total",
Help: "Number of record eval.",
}, []string{"datasource"})
CounterRecordEvalErrorTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "record_eval_error_total",
Help: "Number of record eval error.",
}, []string{"datasource"})
AlertNotifyTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "alert_notify_total",
Help: "Number of send msg.",
}, []string{"channel"})
AlertNotifyErrorTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "alert_notify_error_total",
Help: "Number of send msg.",
}, []string{"channel"})
// 产生的告警总量
CounterAlertsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "alerts_total",
Help: "Total number alert events.",
}, []string{"cluster", "type", "busi_group"})
// 内存中的告警事件队列的长度
GaugeAlertQueueSize := prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "alert_queue_size",
Help: "The size of alert queue.",
})
CounterMuteTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "mute_total",
Help: "Number of mute.",
}, []string{"group", "rule_id", "mute_rule_id", "datasource_id"})
CounterSubEventTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "sub_event_total",
Help: "Number of sub event.",
}, []string{"group"})
CounterHeartbeatErrorTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "heartbeat_error_count",
Help: "Number of heartbeat error.",
}, []string{})
GaugeQuerySeriesCount := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "eval_query_series_count",
Help: "Number of series retrieved from data source after query.",
}, []string{"rule_id", "datasource_id", "ref"})
// 通知记录队列的长度
GaugeNotifyRecordQueueSize := prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "notify_record_queue_size",
Help: "The size of notify record queue.",
})
GaugeRuleEvalDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "rule_eval_duration_ms",
Help: "Duration of rule eval in milliseconds.",
}, []string{"rule_id", "datasource_id"})
CounterVarFillingQuery := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "var_filling_query_total",
Help: "Number of var filling query.",
}, []string{"rule_id", "datasource_id", "ref", "typ"})
prometheus.MustRegister(
CounterAlertsTotal,
GaugeAlertQueueSize,
AlertNotifyTotal,
AlertNotifyErrorTotal,
CounterRuleEval,
CounterQueryDataTotal,
CounterQueryDataErrorTotal,
CounterRecordEval,
CounterRecordEvalErrorTotal,
CounterMuteTotal,
CounterRuleEvalErrorTotal,
CounterHeartbeatErrorTotal,
CounterSubEventTotal,
GaugeQuerySeriesCount,
GaugeRuleEvalDuration,
GaugeNotifyRecordQueueSize,
CounterVarFillingQuery,
)
return &Stats{
CounterAlertsTotal: CounterAlertsTotal,
GaugeAlertQueueSize: GaugeAlertQueueSize,
AlertNotifyTotal: AlertNotifyTotal,
AlertNotifyErrorTotal: AlertNotifyErrorTotal,
CounterRuleEval: CounterRuleEval,
CounterQueryDataTotal: CounterQueryDataTotal,
CounterQueryDataErrorTotal: CounterQueryDataErrorTotal,
CounterRecordEval: CounterRecordEval,
CounterRecordEvalErrorTotal: CounterRecordEvalErrorTotal,
CounterMuteTotal: CounterMuteTotal,
CounterRuleEvalErrorTotal: CounterRuleEvalErrorTotal,
CounterHeartbeatErrorTotal: CounterHeartbeatErrorTotal,
CounterSubEventTotal: CounterSubEventTotal,
GaugeQuerySeriesCount: GaugeQuerySeriesCount,
GaugeRuleEvalDuration: GaugeRuleEvalDuration,
GaugeNotifyRecordQueueSize: GaugeNotifyRecordQueueSize,
CounterVarFillingQuery: CounterVarFillingQuery,
}
}
================================================
FILE: alert/common/key.go
================================================
package common
import (
"encoding/json"
"fmt"
"strings"
"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 {
// target_group in和not in优先特殊处理:匹配通过则继续下一个 filter,匹配失败则整组不匹配
if filter.Key == "target_group" {
// target 字段从 event.JsonTagsAndValue() 中获取的
v, ok := eventTagsMap["target"]
if !ok {
return false
}
if !targetGroupMatch(v, filter) {
return false
}
continue
}
// 普通标签按原逻辑处理
value, has := eventTagsMap[filter.Key]
if !has {
return false
}
if !matchTag(value, filter) {
return false
}
}
return true
}
func MatchGroupsName(groupName string, groupFilter []models.TagFilter) bool {
for _, filter := range groupFilter {
if !matchTag(groupName, filter) {
return false
}
}
return true
}
func matchTag(value string, filter models.TagFilter) bool {
switch filter.Func {
case "==":
return strings.TrimSpace(fmt.Sprintf("%v", filter.Value)) == strings.TrimSpace(value)
case "!=":
return strings.TrimSpace(fmt.Sprintf("%v", filter.Value)) != strings.TrimSpace(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)
}
// unexpected func
return false
}
// targetGroupMatch 处理 target_group 的特殊匹配逻辑
func targetGroupMatch(value string, filter models.TagFilter) bool {
var valueMap map[string]interface{}
if err := json.Unmarshal([]byte(value), &valueMap); err != nil {
return false
}
switch filter.Func {
case "in", "not in":
// float64 类型的 id 切片
filterValueIds, ok := filter.Value.([]interface{})
if !ok {
return false
}
filterValueIdsMap := make(map[float64]struct{})
for _, id := range filterValueIds {
filterValueIdsMap[id.(float64)] = struct{}{}
}
// float64 类型的 groupIds 切片
groupIds, ok := valueMap["group_ids"].([]interface{})
if !ok {
return false
}
// in 只要 groupIds 中有一个在 filterGroupIds 中出现,就返回 true
// not in 则相反
found := false
for _, gid := range groupIds {
if _, found = filterValueIdsMap[gid.(float64)]; found {
break
}
}
if filter.Func == "in" {
return found
}
// filter.Func == "not in"
return !found
case "=~", "!~":
// 正则满足一个就认为 matched
groupNames, ok := valueMap["group_names"].([]interface{})
if !ok {
return false
}
matched := false
for _, gname := range groupNames {
if filter.Regexp.MatchString(fmt.Sprintf("%v", gname)) {
matched = true
break
}
}
if filter.Func == "=~" {
return matched
}
// "!~": 只要有一个匹配就返回 false,否则返回 true
return !matched
default:
return false
}
}
================================================
FILE: alert/dispatch/consume.go
================================================
package dispatch
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/ccfos/nightingale/v6/alert/aconf"
"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/poster"
promsdk "github.com/ccfos/nightingale/v6/pkg/prom"
"github.com/ccfos/nightingale/v6/pkg/tplx"
"github.com/ccfos/nightingale/v6/prom"
"github.com/prometheus/common/model"
"github.com/toolkits/pkg/concurrent/semaphore"
"github.com/toolkits/pkg/logger"
)
type Consumer struct {
alerting aconf.Alerting
ctx *ctx.Context
dispatch *Dispatch
promClients *prom.PromClientMap
alertMuteCache *memsto.AlertMuteCacheType
}
type EventMuteHookFunc func(event *models.AlertCurEvent) bool
var EventMuteHook EventMuteHookFunc = func(event *models.AlertCurEvent) bool { return false }
func InitRegisterQueryFunc(promClients *prom.PromClientMap) {
tplx.RegisterQueryFunc(func(datasourceID int64, promql string) model.Value {
if promClients.IsNil(datasourceID) {
return nil
}
readerClient := promClients.GetCli(datasourceID)
value, _, _ := readerClient.Query(context.Background(), promql, time.Now())
return value
})
}
// 创建一个 Consumer 实例
func NewConsumer(alerting aconf.Alerting, ctx *ctx.Context, dispatch *Dispatch, promClients *prom.PromClientMap, alertMuteCache *memsto.AlertMuteCacheType) *Consumer {
return &Consumer{
alerting: alerting,
ctx: ctx,
dispatch: dispatch,
promClients: promClients,
alertMuteCache: alertMuteCache,
}
}
func (e *Consumer) LoopConsume() {
sema := semaphore.NewSemaphore(e.alerting.NotifyConcurrency)
duration := time.Duration(100) * time.Millisecond
for {
events := queue.EventQueue.PopBackBy(100)
if len(events) == 0 {
time.Sleep(duration)
continue
}
e.consume(events, sema)
}
}
func (e *Consumer) consume(events []interface{}, sema *semaphore.Semaphore) {
for i := range events {
if events[i] == nil {
continue
}
event := events[i].(*models.AlertCurEvent)
sema.Acquire()
go func(event *models.AlertCurEvent) {
defer sema.Release()
e.consumeOne(event)
}(event)
}
}
func (e *Consumer) consumeOne(event *models.AlertCurEvent) {
LogEvent(event, "consume")
eventType := "alert"
if event.IsRecovered {
eventType = "recovery"
}
e.dispatch.Astats.CounterAlertsTotal.WithLabelValues(event.Cluster, eventType, event.GroupName).Inc()
if err := event.ParseRule("rule_name"); err != nil {
logger.Warningf("alert_eval_%d datasource_%d failed to parse rule name: %v", event.RuleId, event.DatasourceId, err)
event.RuleName = fmt.Sprintf("failed to parse rule name: %v", err)
}
if err := event.ParseRule("annotations"); err != nil {
logger.Warningf("alert_eval_%d datasource_%d failed to parse annotations: %v", event.RuleId, event.DatasourceId, err)
event.Annotations = fmt.Sprintf("failed to parse annotations: %v", err)
event.AnnotationsJSON["error"] = event.Annotations
}
e.queryRecoveryVal(event)
if err := event.ParseRule("rule_note"); err != nil {
logger.Warningf("alert_eval_%d datasource_%d failed to parse rule note: %v", event.RuleId, event.DatasourceId, err)
event.RuleNote = fmt.Sprintf("failed to parse rule note: %v", err)
}
e.persist(event)
e.dispatch.HandleEventNotify(event, false)
}
func (e *Consumer) persist(event *models.AlertCurEvent) {
if event.Status != 0 {
return
}
if !e.ctx.IsCenter {
event.DB2FE()
var err error
event.Id, err = poster.PostByUrlsWithResp[int64](e.ctx, "/v1/n9e/event-persist", event)
if err != nil {
logger.Errorf("event:%s persist err:%v", event.Hash, err)
e.dispatch.Astats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", event.DatasourceId), "persist_event", event.GroupName, fmt.Sprintf("%v", event.RuleId)).Inc()
}
return
}
err := models.EventPersist(e.ctx, event)
if err != nil {
logger.Errorf("event:%s persist err:%v", event.Hash, err)
e.dispatch.Astats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", event.DatasourceId), "persist_event", event.GroupName, fmt.Sprintf("%v", event.RuleId)).Inc()
}
}
func (e *Consumer) queryRecoveryVal(event *models.AlertCurEvent) {
if !event.IsRecovered {
return
}
// If the event is a recovery event, execute the recovery_promql query
promql, ok := event.AnnotationsJSON["recovery_promql"]
if !ok {
return
}
promql = strings.TrimSpace(promql)
if promql == "" {
logger.Warningf("alert_eval_%d datasource_%d promql is blank", event.RuleId, event.DatasourceId)
return
}
if e.promClients.IsNil(event.DatasourceId) {
logger.Warningf("alert_eval_%d datasource_%d error reader client is nil", event.RuleId, event.DatasourceId)
return
}
readerClient := e.promClients.GetCli(event.DatasourceId)
var warnings promsdk.Warnings
value, warnings, err := readerClient.Query(e.ctx.Ctx, promql, time.Now())
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d promql:%s, error:%v", event.RuleId, event.DatasourceId, promql, err)
event.AnnotationsJSON["recovery_promql_error"] = fmt.Sprintf("promql:%s error:%v", promql, err)
b, err := json.Marshal(event.AnnotationsJSON)
if err != nil {
event.AnnotationsJSON = make(map[string]string)
event.AnnotationsJSON["error"] = fmt.Sprintf("failed to parse annotations: %v", err)
} else {
event.Annotations = string(b)
}
return
}
if len(warnings) > 0 {
logger.Errorf("alert_eval_%d datasource_%d promql:%s, warnings:%v", event.RuleId, event.DatasourceId, promql, warnings)
}
anomalyPoints := models.ConvertAnomalyPoints(value)
if len(anomalyPoints) == 0 {
logger.Warningf("alert_eval_%d datasource_%d promql:%s, result is empty", event.RuleId, event.DatasourceId, promql)
event.AnnotationsJSON["recovery_promql_error"] = fmt.Sprintf("promql:%s error:%s", promql, "result is empty")
} else {
event.AnnotationsJSON["recovery_value"] = fmt.Sprintf("%v", anomalyPoints[0].Value)
}
b, err := json.Marshal(event.AnnotationsJSON)
if err != nil {
event.AnnotationsJSON = make(map[string]string)
event.AnnotationsJSON["error"] = fmt.Sprintf("failed to parse annotations: %v", err)
} else {
event.Annotations = string(b)
}
}
================================================
FILE: alert/dispatch/dispatch.go
================================================
package dispatch
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"html/template"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/ccfos/nightingale/v6/alert/aconf"
"github.com/ccfos/nightingale/v6/alert/astats"
"github.com/ccfos/nightingale/v6/alert/common"
"github.com/ccfos/nightingale/v6/alert/pipeline"
"github.com/ccfos/nightingale/v6/alert/pipeline/engine"
"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"
)
var ShouldSkipNotify func(*ctx.Context, *models.AlertCurEvent, int64) bool
var SendByNotifyRule func(*ctx.Context, *memsto.UserCacheType, *memsto.UserGroupCacheType, *memsto.NotifyChannelCacheType, *memsto.CvalCache,
[]*models.AlertCurEvent, int64, *models.NotifyConfig, *models.NotifyChannelConfig, *models.MessageTemplate)
var EventProcessorCache *memsto.EventProcessorCacheType
func init() {
ShouldSkipNotify = shouldSkipNotify
SendByNotifyRule = SendNotifyRuleMessage
}
type Dispatch struct {
alertRuleCache *memsto.AlertRuleCacheType
userCache *memsto.UserCacheType
userGroupCache *memsto.UserGroupCacheType
alertSubscribeCache *memsto.AlertSubscribeCacheType
targetCache *memsto.TargetCacheType
notifyConfigCache *memsto.NotifyConfigCacheType
taskTplsCache *memsto.TaskTplCache
configCvalCache *memsto.CvalCache
notifyRuleCache *memsto.NotifyRuleCacheType
notifyChannelCache *memsto.NotifyChannelCacheType
messageTemplateCache *memsto.MessageTemplateCacheType
eventProcessorCache *memsto.EventProcessorCacheType
alerting aconf.Alerting
Senders map[string]sender.Sender
CallBacks map[string]sender.CallBacker
tpls map[string]*template.Template
ExtraSenders map[string]sender.Sender
BeforeSenderHook func(*models.AlertCurEvent) bool
ctx *ctx.Context
Astats *astats.Stats
RwLock sync.RWMutex
}
// 创建一个 Notify 实例
func NewDispatch(alertRuleCache *memsto.AlertRuleCacheType, userCache *memsto.UserCacheType, userGroupCache *memsto.UserGroupCacheType,
alertSubscribeCache *memsto.AlertSubscribeCacheType, targetCache *memsto.TargetCacheType, notifyConfigCache *memsto.NotifyConfigCacheType,
taskTplsCache *memsto.TaskTplCache, notifyRuleCache *memsto.NotifyRuleCacheType, notifyChannelCache *memsto.NotifyChannelCacheType,
messageTemplateCache *memsto.MessageTemplateCacheType, eventProcessorCache *memsto.EventProcessorCacheType, configCvalCache *memsto.CvalCache, alerting aconf.Alerting, c *ctx.Context, astats *astats.Stats) *Dispatch {
notify := &Dispatch{
alertRuleCache: alertRuleCache,
userCache: userCache,
userGroupCache: userGroupCache,
alertSubscribeCache: alertSubscribeCache,
targetCache: targetCache,
notifyConfigCache: notifyConfigCache,
taskTplsCache: taskTplsCache,
notifyRuleCache: notifyRuleCache,
notifyChannelCache: notifyChannelCache,
messageTemplateCache: messageTemplateCache,
eventProcessorCache: eventProcessorCache,
configCvalCache: configCvalCache,
alerting: alerting,
Senders: make(map[string]sender.Sender),
tpls: make(map[string]*template.Template),
ExtraSenders: make(map[string]sender.Sender),
BeforeSenderHook: func(*models.AlertCurEvent) bool { return true },
ctx: c,
Astats: astats,
}
pipeline.Init()
EventProcessorCache = eventProcessorCache
// 设置通知记录回调函数
notifyChannelCache.SetNotifyRecordFunc(sender.NotifyRecord)
return notify
}
func (e *Dispatch) ReloadTpls() error {
err := e.reloadTpls()
if err != nil {
logger.Errorf("failed to reload tpls: %v", err)
}
duration := time.Duration(9000) * time.Millisecond
for {
time.Sleep(duration)
if err := e.reloadTpls(); err != nil {
logger.Warning("failed to reload tpls:", err)
}
}
}
func (e *Dispatch) reloadTpls() error {
tmpTpls, err := models.ListTpls(e.ctx)
if err != nil {
return err
}
smtp := e.notifyConfigCache.GetSMTP()
senders := map[string]sender.Sender{
models.Email: sender.NewSender(models.Email, tmpTpls, smtp),
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),
models.FeishuCard: sender.NewSender(models.FeishuCard, tmpTpls),
models.Lark: sender.NewSender(models.Lark, tmpTpls),
models.LarkCard: sender.NewSender(models.LarkCard, tmpTpls),
}
// domain -> Callback()
callbacks := map[string]sender.CallBacker{
models.DingtalkDomain: sender.NewCallBacker(models.DingtalkDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.WecomDomain: sender.NewCallBacker(models.WecomDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.FeishuDomain: sender.NewCallBacker(models.FeishuDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.TelegramDomain: sender.NewCallBacker(models.TelegramDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.FeishuCardDomain: sender.NewCallBacker(models.FeishuCardDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.IbexDomain: sender.NewCallBacker(models.IbexDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.LarkDomain: sender.NewCallBacker(models.LarkDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.DefaultDomain: sender.NewCallBacker(models.DefaultDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
models.LarkCardDomain: sender.NewCallBacker(models.LarkCardDomain, e.targetCache, e.userCache, e.taskTplsCache, tmpTpls),
}
e.RwLock.RLock()
for channelName, extraSender := range e.ExtraSenders {
senders[channelName] = extraSender
}
e.RwLock.RUnlock()
e.RwLock.Lock()
e.tpls = tmpTpls
e.Senders = senders
e.CallBacks = callbacks
e.RwLock.Unlock()
return nil
}
func (e *Dispatch) HandleEventWithNotifyRule(eventOrigin *models.AlertCurEvent) {
if len(eventOrigin.NotifyRuleIds) > 0 {
for _, notifyRuleId := range eventOrigin.NotifyRuleIds {
// 深拷贝新的 event,避免并发修改 event 冲突
eventCopy := eventOrigin.DeepCopy()
logger.Infof("notify rule ids: %v, event: %s", notifyRuleId, eventCopy.Hash)
notifyRule := e.notifyRuleCache.Get(notifyRuleId)
if notifyRule == nil {
continue
}
if !notifyRule.Enable {
continue
}
eventCopy.NotifyRuleId = notifyRuleId
eventCopy.NotifyRuleName = notifyRule.Name
eventCopy = HandleEventPipeline(notifyRule.PipelineConfigs, eventOrigin, eventCopy, e.eventProcessorCache, e.ctx, notifyRuleId, "notify_rule")
if eventCopy == nil {
continue
}
if ShouldSkipNotify(e.ctx, eventCopy, notifyRuleId) {
logger.Infof("notify_id: %d, event:%s, should skip notify", notifyRuleId, eventCopy.Hash)
continue
}
// notify
for i := range notifyRule.NotifyConfigs {
err := NotifyRuleMatchCheck(¬ifyRule.NotifyConfigs[i], eventCopy)
if err != nil {
logger.Errorf("notify_id: %d, event:%s, channel_id:%d, template_id: %d, notify_config:%+v, err:%v", notifyRuleId, eventCopy.Hash, notifyRule.NotifyConfigs[i].ChannelID, notifyRule.NotifyConfigs[i].TemplateID, notifyRule.NotifyConfigs[i], err)
continue
}
notifyChannel := e.notifyChannelCache.Get(notifyRule.NotifyConfigs[i].ChannelID)
messageTemplate := e.messageTemplateCache.Get(notifyRule.NotifyConfigs[i].TemplateID)
if notifyChannel == nil {
sender.NotifyRecord(e.ctx, []*models.AlertCurEvent{eventCopy}, notifyRuleId, fmt.Sprintf("notify_channel_id:%d", notifyRule.NotifyConfigs[i].ChannelID), "", "", errors.New("notify_channel not found"))
logger.Warningf("notify_id: %d, event:%s, channel_id:%d, template_id: %d, notify_channel not found", notifyRuleId, eventCopy.Hash, notifyRule.NotifyConfigs[i].ChannelID, notifyRule.NotifyConfigs[i].TemplateID)
continue
}
if notifyChannel.RequestType != "flashduty" && notifyChannel.RequestType != "pagerduty" && messageTemplate == nil {
logger.Warningf("notify_id: %d, channel_name: %v, event:%s, template_id: %d, message_template not found", notifyRuleId, notifyChannel.Ident, eventCopy.Hash, notifyRule.NotifyConfigs[i].TemplateID)
sender.NotifyRecord(e.ctx, []*models.AlertCurEvent{eventCopy}, notifyRuleId, notifyChannel.Name, "", "", errors.New("message_template not found"))
continue
}
go SendByNotifyRule(e.ctx, e.userCache, e.userGroupCache, e.notifyChannelCache, e.configCvalCache, []*models.AlertCurEvent{eventCopy}, notifyRuleId, ¬ifyRule.NotifyConfigs[i], notifyChannel, messageTemplate)
}
}
}
}
func shouldSkipNotify(ctx *ctx.Context, event *models.AlertCurEvent, notifyRuleId int64) bool {
if event == nil {
// 如果 eventCopy 为 nil,说明 eventCopy 被 processor drop 掉了, 不再发送通知
return true
}
if event.IsRecovered && event.NotifyRecovered == 0 {
// 如果 eventCopy 是恢复事件,且 NotifyRecovered 为 0,则不发送通知
return true
}
return false
}
func HandleEventPipeline(pipelineConfigs []models.PipelineConfig, eventOrigin, event *models.AlertCurEvent, eventProcessorCache *memsto.EventProcessorCacheType, ctx *ctx.Context, id int64, from string) *models.AlertCurEvent {
workflowEngine := engine.NewWorkflowEngine(ctx)
for _, pipelineConfig := range pipelineConfigs {
if !pipelineConfig.Enable {
continue
}
eventPipeline := eventProcessorCache.Get(pipelineConfig.PipelineId)
if eventPipeline == nil {
logger.Warningf("processor_by_%s_id:%d pipeline_id:%d, event pipeline not found, event: %s", from, id, pipelineConfig.PipelineId, event.Hash)
continue
}
if !PipelineApplicable(eventPipeline, event) {
logger.Debugf("processor_by_%s_id:%d pipeline_id:%d, event pipeline not applicable, event: %s", from, id, pipelineConfig.PipelineId, event.Hash)
continue
}
// 统一使用工作流引擎执行(兼容线性模式和工作流模式)
triggerCtx := &models.WorkflowTriggerContext{
Mode: models.TriggerModeEvent,
TriggerBy: from + "_" + strconv.FormatInt(id, 10),
}
resultEvent, result, err := workflowEngine.Execute(eventPipeline, event, triggerCtx)
if err != nil {
logger.Errorf("processor_by_%s_id:%d pipeline_id:%d, pipeline execute error: %v", from, id, pipelineConfig.PipelineId, err)
continue
}
if resultEvent == nil {
logger.Infof("processor_by_%s_id:%d pipeline_id:%d, event dropped, event: %s", from, id, pipelineConfig.PipelineId, eventOrigin.Hash)
if from == "notify_rule" {
sender.NotifyRecord(ctx, []*models.AlertCurEvent{eventOrigin}, id, "", "", result.Message, fmt.Errorf("processor_by_%s_id:%d pipeline_id:%d, drop by pipeline", from, id, pipelineConfig.PipelineId))
}
return nil
}
event = resultEvent
logger.Infof("processor_by_%s_id:%d pipeline_id:%d, pipeline executed, status:%s, message:%s", from, id, pipelineConfig.PipelineId, result.Status, result.Message)
}
event.FE2DB()
event.FillTagsMap()
return event
}
func PipelineApplicable(pipeline *models.EventPipeline, event *models.AlertCurEvent) bool {
if pipeline == nil {
return true
}
if !pipeline.FilterEnable {
return true
}
tagMatch := true
if len(pipeline.LabelFilters) > 0 {
// Deep copy to avoid concurrent map writes on cached objects
labelFiltersCopy := make([]models.TagFilter, len(pipeline.LabelFilters))
copy(labelFiltersCopy, pipeline.LabelFilters)
for i := range labelFiltersCopy {
if labelFiltersCopy[i].Func == "" {
labelFiltersCopy[i].Func = labelFiltersCopy[i].Op
}
}
tagFilters, err := models.ParseTagFilter(labelFiltersCopy)
if err != nil {
logger.Errorf("pipeline applicable failed to parse tag filter: %v event:%s pipeline:%+v", err, event.Hash, pipeline)
return false
}
tagMatch = common.MatchTags(event.TagsMap, tagFilters)
}
attributesMatch := true
if len(pipeline.AttrFilters) > 0 {
// Deep copy to avoid concurrent map writes on cached objects
attrFiltersCopy := make([]models.TagFilter, len(pipeline.AttrFilters))
copy(attrFiltersCopy, pipeline.AttrFilters)
tagFilters, err := models.ParseTagFilter(attrFiltersCopy)
if err != nil {
logger.Errorf("pipeline applicable failed to parse tag filter: %v event:%s pipeline:%+v err:%v", tagFilters, event.Hash, pipeline, err)
return false
}
attributesMatch = common.MatchTags(event.JsonTagsAndValue(), tagFilters)
}
return tagMatch && attributesMatch
}
func NotifyRuleMatchCheck(notifyConfig *models.NotifyConfig, event *models.AlertCurEvent) error {
tm := time.Unix(event.TriggerTime, 0)
triggerTime := tm.Format("15:04")
triggerWeek := int(tm.Weekday())
timeMatch := false
if len(notifyConfig.TimeRanges) == 0 {
timeMatch = true
}
for j := range notifyConfig.TimeRanges {
if timeMatch {
break
}
enableStime := notifyConfig.TimeRanges[j].Start
enableEtime := notifyConfig.TimeRanges[j].End
enableDaysOfWeek := notifyConfig.TimeRanges[j].Week
length := len(enableDaysOfWeek)
// enableStime,enableEtime,enableDaysOfWeek三者长度肯定相同,这里循环一个即可
for i := 0; i < length; i++ {
if enableDaysOfWeek[i] != triggerWeek {
continue
}
if enableStime < enableEtime {
if enableEtime == "23:59" {
// 02:00-23:59,这种情况做个特殊处理,相当于左闭右闭区间了
if triggerTime < enableStime {
// mute, 即没生效
continue
}
} else {
// 02:00-04:00 或者 02:00-24:00
if triggerTime < enableStime || triggerTime >= enableEtime {
// mute, 即没生效
continue
}
}
} else if enableStime > enableEtime {
// 21:00-09:00
if triggerTime < enableStime && triggerTime >= enableEtime {
// mute, 即没生效
continue
}
}
// 到这里说明当前时刻在告警规则的某组生效时间范围内,即没有 mute,直接返回 false
timeMatch = true
break
}
}
if !timeMatch {
return fmt.Errorf("event time not match time filter")
}
severityMatch := false
for i := range notifyConfig.Severities {
if notifyConfig.Severities[i] == event.Severity {
severityMatch = true
}
}
if !severityMatch {
return fmt.Errorf("event severity not match severity filter")
}
tagMatch := true
if len(notifyConfig.LabelKeys) > 0 {
// Deep copy to avoid concurrent map writes on cached objects
labelKeysCopy := make([]models.TagFilter, len(notifyConfig.LabelKeys))
copy(labelKeysCopy, notifyConfig.LabelKeys)
for i := range labelKeysCopy {
if labelKeysCopy[i].Func == "" {
labelKeysCopy[i].Func = labelKeysCopy[i].Op
}
}
tagFilters, err := models.ParseTagFilter(labelKeysCopy)
if err != nil {
logger.Errorf("notify send failed to parse tag filter: %v event:%s notify_config:%+v", err, event.Hash, notifyConfig)
return fmt.Errorf("failed to parse tag filter: %v", err)
}
tagMatch = common.MatchTags(event.TagsMap, tagFilters)
}
if !tagMatch {
return fmt.Errorf("event tag not match tag filter")
}
attributesMatch := true
if len(notifyConfig.Attributes) > 0 {
// Deep copy to avoid concurrent map writes on cached objects
attributesCopy := make([]models.TagFilter, len(notifyConfig.Attributes))
copy(attributesCopy, notifyConfig.Attributes)
tagFilters, err := models.ParseTagFilter(attributesCopy)
if err != nil {
logger.Errorf("notify send failed to parse tag filter: %v event:%s notify_config:%+v err:%v", tagFilters, event.Hash, notifyConfig, err)
return fmt.Errorf("failed to parse tag filter: %v", err)
}
attributesMatch = common.MatchTags(event.JsonTagsAndValue(), tagFilters)
}
if !attributesMatch {
return fmt.Errorf("event attributes not match attributes filter")
}
logger.Infof("notify send timeMatch:%v severityMatch:%v tagMatch:%v attributesMatch:%v event:%s notify_config:%+v", timeMatch, severityMatch, tagMatch, attributesMatch, event.Hash, notifyConfig)
return nil
}
func GetNotifyConfigParams(notifyConfig *models.NotifyConfig, contactKey string, userCache *memsto.UserCacheType, userGroupCache *memsto.UserGroupCacheType) ([]string, []int64, []string, map[string]string) {
customParams := make(map[string]string)
var flashDutyChannelIDs []int64
var pagerDutyRoutingKeys []string
var userInfoParams models.CustomParams
for key, value := range notifyConfig.Params {
switch key {
case "user_ids", "user_group_ids", "ids":
if data, err := json.Marshal(value); err == nil {
var ids []int64
if json.Unmarshal(data, &ids) == nil {
if key == "user_ids" {
userInfoParams.UserIDs = ids
} else if key == "user_group_ids" {
userInfoParams.UserGroupIDs = ids
} else if key == "ids" {
flashDutyChannelIDs = ids
}
}
}
case "pagerduty_integration_keys", "pagerduty_integration_ids":
if key == "pagerduty_integration_ids" {
// 不处理ids,直接跳过,这个字段只给前端标记用
continue
}
if data, err := json.Marshal(value); err == nil {
var keys []string
if json.Unmarshal(data, &keys) == nil {
pagerDutyRoutingKeys = keys
break
}
}
default:
// 避免直接 value.(string) 导致 panic,支持多种类型并统一为字符串
customParams[key] = value.(string)
}
}
if len(userInfoParams.UserIDs) == 0 && len(userInfoParams.UserGroupIDs) == 0 {
return []string{}, flashDutyChannelIDs, pagerDutyRoutingKeys, customParams
}
userIds := make([]int64, 0)
userIds = append(userIds, userInfoParams.UserIDs...)
if len(userInfoParams.UserGroupIDs) > 0 {
userGroups := userGroupCache.GetByUserGroupIds(userInfoParams.UserGroupIDs)
for _, userGroup := range userGroups {
userIds = append(userIds, userGroup.UserIds...)
}
}
users := userCache.GetByUserIds(userIds)
visited := make(map[int64]bool)
sendtos := make([]string, 0)
for _, user := range users {
if visited[user.Id] {
continue
}
var sendto string
if contactKey == "phone" {
sendto = user.Phone
} else if contactKey == "email" {
sendto = user.Email
} else {
sendto, _ = user.ExtractToken(contactKey)
}
if sendto == "" {
continue
}
sendtos = append(sendtos, sendto)
visited[user.Id] = true
}
return sendtos, flashDutyChannelIDs, pagerDutyRoutingKeys, customParams
}
func SendNotifyRuleMessage(ctx *ctx.Context, userCache *memsto.UserCacheType, userGroupCache *memsto.UserGroupCacheType, notifyChannelCache *memsto.NotifyChannelCacheType, configCvalCache *memsto.CvalCache,
events []*models.AlertCurEvent, notifyRuleId int64, notifyConfig *models.NotifyConfig, notifyChannel *models.NotifyChannelConfig, messageTemplate *models.MessageTemplate) {
if len(events) == 0 {
logger.Errorf("notify_id: %d events is empty", notifyRuleId)
return
}
siteInfo := configCvalCache.GetSiteInfo()
tplContent := make(map[string]interface{})
if notifyChannel.RequestType != "flashduty" {
tplContent = messageTemplate.RenderEvent(events, siteInfo.SiteUrl)
}
var contactKey string
if notifyChannel.ParamConfig != nil && notifyChannel.ParamConfig.UserInfo != nil {
contactKey = notifyChannel.ParamConfig.UserInfo.ContactKey
}
sendtos, flashDutyChannelIDs, pagerdutyRoutingKeys, customParams := GetNotifyConfigParams(notifyConfig, contactKey, userCache, userGroupCache)
switch notifyChannel.RequestType {
case "flashduty":
if len(flashDutyChannelIDs) == 0 {
flashDutyChannelIDs = []int64{0} // 如果 flashduty 通道没有配置,则使用 0, 给 SendFlashDuty 判断使用, 不给 flashduty 传 channel_id 参数
}
for i := range flashDutyChannelIDs {
start := time.Now()
respBody, err := notifyChannel.SendFlashDuty(events, flashDutyChannelIDs[i], notifyChannelCache.GetHttpClient(notifyChannel.ID))
respBody = fmt.Sprintf("send_time: %s duration: %d ms %s", time.Now().Format("2006-01-02 15:04:05"), time.Since(start).Milliseconds(), respBody)
logger.Infof("duty_sender notify_id: %d, channel_name: %v, event:%s, IntegrationUrl: %v dutychannel_id: %v, respBody: %v, err: %v", notifyRuleId, notifyChannel.Name, events[0].Hash, notifyChannel.RequestConfig.FlashDutyRequestConfig.IntegrationUrl, flashDutyChannelIDs[i], respBody, err)
sender.NotifyRecord(ctx, events, notifyRuleId, notifyChannel.Name, strconv.FormatInt(flashDutyChannelIDs[i], 10), respBody, err)
}
case "pagerduty":
for _, routingKey := range pagerdutyRoutingKeys {
start := time.Now()
respBody, err := notifyChannel.SendPagerDuty(events, routingKey, siteInfo.SiteUrl, notifyChannelCache.GetHttpClient(notifyChannel.ID))
respBody = fmt.Sprintf("send_time: %s duration: %d ms %s", time.Now().Format("2006-01-02 15:04:05"), time.Since(start).Milliseconds(), respBody)
logger.Infof("pagerduty_sender notify_id: %d, channel_name: %v, event:%s, respBody: %v, err: %v", notifyRuleId, notifyChannel.Name, events[0].Hash, respBody, err)
sender.NotifyRecord(ctx, events, notifyRuleId, notifyChannel.Name, "", respBody, err)
}
case "http":
// 使用队列模式处理 http 通知
// 创建通知任务
task := &memsto.NotifyTask{
Events: events,
NotifyRuleId: notifyRuleId,
NotifyChannel: notifyChannel,
TplContent: tplContent,
CustomParams: customParams,
Sendtos: sendtos,
}
// 将任务加入队列
success := notifyChannelCache.EnqueueNotifyTask(task)
if !success {
logger.Errorf("failed to enqueue notify task for channel %d, notify_id: %d", notifyChannel.ID, notifyRuleId)
// 如果入队失败,记录错误通知
sender.NotifyRecord(ctx, events, notifyRuleId, notifyChannel.Name, getSendTarget(customParams, sendtos), "", errors.New("failed to enqueue notify task, queue is full"))
}
case "smtp":
notifyChannel.SendEmail(notifyRuleId, events, tplContent, sendtos, notifyChannelCache.GetSmtpClient(notifyChannel.ID))
case "script":
start := time.Now()
target, res, err := notifyChannel.SendScript(events, tplContent, customParams, sendtos)
res = fmt.Sprintf("send_time: %s duration: %d ms %s", time.Now().Format("2006-01-02 15:04:05"), time.Since(start).Milliseconds(), res)
logger.Infof("script_sender notify_id: %d, channel_name: %v, event:%s, tplContent:%s, customParams:%v, target:%s, res:%s, err:%v", notifyRuleId, notifyChannel.Name, events[0].Hash, tplContent, customParams, target, res, err)
sender.NotifyRecord(ctx, events, notifyRuleId, notifyChannel.Name, target, res, err)
default:
logger.Warningf("notify_id: %d, channel_name: %v, event:%s send type not found", notifyRuleId, notifyChannel.Name, events[0].Hash)
}
}
func NeedBatchContacts(requestConfig *models.HTTPRequestConfig) bool {
b, _ := json.Marshal(requestConfig)
return strings.Contains(string(b), "$sendtos")
}
// HandleEventNotify 处理event事件的主逻辑
// event: 告警/恢复事件
// isSubscribe: 告警事件是否由subscribe的配置产生
func (e *Dispatch) HandleEventNotify(event *models.AlertCurEvent, isSubscribe bool) {
go e.HandleEventWithNotifyRule(event)
if event.IsRecovered && event.NotifyRecovered == 0 {
return
}
if !isSubscribe {
go sender.SendStaticGlobalWebhook(e.ctx, event.DeepCopy(), e.Astats)
}
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))
}
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() {
return
}
if !sub.MatchCluster(event.DatasourceId) {
return
}
if !sub.MatchProd(event.RuleProd) {
return
}
if !sub.MatchCate(event.Cate) {
return
}
if !common.MatchTags(event.TagsMap, sub.ITags) {
return
}
// event BusiGroups filter
if !common.MatchGroupsName(event.GroupName, sub.IBusiGroups) {
return
}
if sub.ForDuration > (event.TriggerTime - event.FirstTriggerTime) {
return
}
if len(sub.SeveritiesJson) != 0 {
match := false
for _, s := range sub.SeveritiesJson {
if s == event.Severity || s == 0 {
match = true
break
}
}
if !match {
return
}
}
e.Astats.CounterSubEventTotal.WithLabelValues(event.GroupName).Inc()
sub.ModifyEvent(&event)
event.SubRuleId = sub.Id
LogEvent(&event, "subscribe")
e.HandleEventNotify(&event, true)
}
func (e *Dispatch) Send(rule *models.AlertRule, event *models.AlertCurEvent, notifyTarget *NotifyTarget, isSubscribe bool) {
needSend := e.BeforeSenderHook(event)
if needSend {
for channel, uids := range notifyTarget.ToChannelUserMap() {
msgCtx := sender.BuildMessageContext(e.ctx, rule, []*models.AlertCurEvent{event},
uids, e.userCache, e.Astats)
e.RwLock.RLock()
s := e.Senders[channel]
e.RwLock.RUnlock()
if s == nil {
logger.Debugf("no sender for channel: %s", channel)
continue
}
var event *models.AlertCurEvent
if len(msgCtx.Events) > 0 {
event = msgCtx.Events[0]
}
logger.Debugf("send to channel:%s event:%s users:%+v", channel, event.Hash, msgCtx.Users)
s.Send(msgCtx)
}
}
// handle event callbacks
e.SendCallbacks(rule, notifyTarget, event)
// handle global webhooks
if !event.OverrideGlobalWebhook() {
if e.alerting.WebhookBatchSend {
sender.BatchSendWebhooks(e.ctx, notifyTarget.ToWebhookMap(), event, e.Astats)
} else {
sender.SingleSendWebhooks(e.ctx, notifyTarget.ToWebhookMap(), event, e.Astats)
}
}
// handle plugin call
go sender.MayPluginNotify(e.ctx, e.genNoticeBytes(event), e.notifyConfigCache.
GetNotifyScript(), e.Astats, event)
if !isSubscribe {
// handle ibex callbacks
e.HandleIbex(rule, event)
}
}
func (e *Dispatch) SendCallbacks(rule *models.AlertRule, notifyTarget *NotifyTarget, event *models.AlertCurEvent) {
uids := notifyTarget.ToUidList()
urls := notifyTarget.ToCallbackList()
whMap := notifyTarget.ToWebhookMap()
ogw := event.OverrideGlobalWebhook()
for _, urlStr := range urls {
if len(urlStr) == 0 {
continue
}
cbCtx := sender.BuildCallBackContext(e.ctx, urlStr, rule, []*models.AlertCurEvent{event}, uids, e.userCache, e.alerting.WebhookBatchSend, e.Astats)
if wh, ok := whMap[cbCtx.CallBackURL]; !ogw && ok && wh.Enable {
logger.Debugf("SendCallbacks: webhook[%s] is in global conf.", cbCtx.CallBackURL)
continue
}
if strings.HasPrefix(urlStr, "${ibex}") {
e.CallBacks[models.IbexDomain].CallBack(cbCtx)
continue
}
if !(strings.HasPrefix(urlStr, "http://") || strings.HasPrefix(urlStr, "https://")) {
cbCtx.CallBackURL = "http://" + urlStr
}
parsedURL, err := url.Parse(urlStr)
if err != nil {
logger.Errorf("SendCallbacks: failed to url.Parse(urlStr=%s): %v", urlStr, err)
continue
}
// process feishu card
if parsedURL.Host == models.FeishuDomain && parsedURL.Query().Get("card") == "1" {
e.CallBacks[models.FeishuCardDomain].CallBack(cbCtx)
continue
}
// process lark card
if parsedURL.Host == models.LarkDomain && parsedURL.Query().Get("card") == "1" {
e.CallBacks[models.LarkCardDomain].CallBack(cbCtx)
continue
}
callBacker, ok := e.CallBacks[parsedURL.Host]
if ok {
callBacker.CallBack(cbCtx)
} else {
e.CallBacks[models.DefaultDomain].CallBack(cbCtx)
}
}
}
func (e *Dispatch) HandleIbex(rule *models.AlertRule, event *models.AlertCurEvent) {
// 解析 RuleConfig 字段
var ruleConfig struct {
TaskTpls []*models.Tpl `json:"task_tpls"`
}
json.Unmarshal([]byte(rule.RuleConfig), &ruleConfig)
if event.IsRecovered {
// 恢复事件不需要走故障自愈的逻辑
return
}
for _, t := range ruleConfig.TaskTpls {
if t.TplId == 0 {
continue
}
if len(t.Host) == 0 {
sender.CallIbex(e.ctx, t.TplId, event.TargetIdent,
e.taskTplsCache, e.targetCache, e.userCache, event, "")
continue
}
for _, host := range t.Host {
sender.CallIbex(e.ctx, t.TplId, host,
e.taskTplsCache, e.targetCache, e.userCache, event, "")
}
}
}
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
}
func getSendTarget(customParams map[string]string, sendtos []string) string {
if len(customParams) == 0 {
return strings.Join(sendtos, ",")
}
values := make([]string, 0)
for _, value := range customParams {
runes := []rune(value)
if len(runes) <= 4 {
values = append(values, value)
} else {
maskedValue := string(runes[:len(runes)-4]) + "****"
values = append(values, maskedValue)
}
}
return strings.Join(values, ",")
}
================================================
FILE: alert/dispatch/log.go
================================================
package dispatch
import (
"github.com/ccfos/nightingale/v6/models"
"github.com/toolkits/pkg/logger"
)
func LogEvent(event *models.AlertCurEvent, location string, err ...error) {
status := "triggered"
if event.IsRecovered {
status = "recovered"
}
message := ""
if len(err) > 0 && err[0] != nil {
message = "error_message: " + err[0].Error()
}
logger.Infof(
"alert_eval_%d event(%s %s) %s: sub_id:%d notify_rule_ids:%v cluster:%s %v%s@%d last_eval_time:%d %s",
event.RuleId,
event.Hash,
status,
location,
event.SubRuleId,
event.NotifyRuleIds,
event.Cluster,
event.TagsJSON,
event.TriggerValue,
event.TriggerTime,
event.LastEvalTime,
message,
)
}
================================================
FILE: alert/dispatch/notify_channel.go
================================================
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
}
}
}
================================================
FILE: alert/dispatch/notify_target.go
================================================
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) ToWebhookMap() map[string]*models.Webhook {
return s.webhooks
}
func (s *NotifyTarget) ToUidList() []int64 {
uids := make([]int64, 0, len(s.userMap))
for uid, _ := range s.userMap {
uids = append(uids, uid)
}
return uids
}
// 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.notifyConfigCache.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
}
================================================
FILE: alert/eval/alert_rule.go
================================================
package eval
import (
"context"
"fmt"
"strconv"
"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/datasource/commons/eslike"
"github.com/ccfos/nightingale/v6/memsto"
"github.com/ccfos/nightingale/v6/pkg/ctx"
"github.com/ccfos/nightingale/v6/prom"
"github.com/toolkits/pkg/logger"
)
type Scheduler struct {
// key: hash
alertRules map[string]*AlertRuleWorker
ExternalProcessors *process.ExternalProcessorsType
aconf aconf.Alert
alertRuleCache *memsto.AlertRuleCacheType
targetCache *memsto.TargetCacheType
targetsOfAlertRuleCache *memsto.TargetsOfAlertRuleCacheType
busiGroupCache *memsto.BusiGroupCacheType
alertMuteCache *memsto.AlertMuteCacheType
datasourceCache *memsto.DatasourceCacheType
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, toarc *memsto.TargetsOfAlertRuleCacheType,
busiGroupCache *memsto.BusiGroupCacheType, alertMuteCache *memsto.AlertMuteCacheType, datasourceCache *memsto.DatasourceCacheType,
promClients *prom.PromClientMap, naming *naming.Naming, ctx *ctx.Context, stats *astats.Stats) *Scheduler {
scheduler := &Scheduler{
aconf: aconf,
alertRules: make(map[string]*AlertRuleWorker),
ExternalProcessors: externalProcessors,
alertRuleCache: arc,
targetCache: targetCache,
targetsOfAlertRuleCache: toarc,
busiGroupCache: busiGroupCache,
alertMuteCache: alertMuteCache,
datasourceCache: datasourceCache,
promClients: promClients,
naming: naming,
ctx: ctx,
stats: stats,
}
eslike.SetEsIndexPatternCacheType(memsto.NewEsIndexPatternCacheType(ctx))
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
}
ruleType := rule.GetRuleType()
if rule.IsPrometheusRule() || rule.IsInnerRule() {
datasourceIds := s.datasourceCache.GetIDsByDsCateAndQueries(rule.Cate, rule.DatasourceQueries)
for _, dsId := range datasourceIds {
if !naming.DatasourceHashRing.IsHit(strconv.FormatInt(dsId, 10), fmt.Sprintf("%d", rule.Id), s.aconf.Heartbeat.Endpoint) {
continue
}
ds := s.datasourceCache.GetById(dsId)
if ds == nil {
logger.Debugf("alert_eval_%d datasource %d not found", rule.Id, dsId)
continue
}
if ds.PluginType != ruleType {
logger.Debugf("alert_eval_%d datasource %d category is %s not %s", rule.Id, dsId, ds.PluginType, ruleType)
continue
}
if ds.Status != "enabled" {
logger.Debugf("alert_eval_%d datasource %d status is %s", rule.Id, dsId, ds.Status)
continue
}
processor := process.NewProcessor(s.aconf.Heartbeat.EngineName, rule, dsId, s.alertRuleCache, s.targetCache, s.targetsOfAlertRuleCache, s.busiGroupCache, s.alertMuteCache, s.datasourceCache, s.ctx, s.stats)
alertRule := NewAlertRuleWorker(rule, dsId, processor, s.promClients, s.ctx)
alertRuleWorkers[alertRule.Hash()] = alertRule
}
} else if rule.IsHostRule() {
// all host rule will be processed by center instance
if !naming.DatasourceHashRing.IsHit(s.aconf.Heartbeat.EngineName, strconv.FormatInt(rule.Id, 10), s.aconf.Heartbeat.Endpoint) {
continue
}
processor := process.NewProcessor(s.aconf.Heartbeat.EngineName, rule, 0, s.alertRuleCache, s.targetCache, s.targetsOfAlertRuleCache, s.busiGroupCache, s.alertMuteCache, s.datasourceCache, 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
dsIds := s.datasourceCache.GetIDsByDsCateAndQueries(rule.Cate, rule.DatasourceQueries)
for _, dsId := range dsIds {
ds := s.datasourceCache.GetById(dsId)
if ds == nil {
logger.Debugf("alert_eval_%d datasource %d not found", rule.Id, dsId)
continue
}
if ds.Status != "enabled" {
logger.Debugf("alert_eval_%d datasource %d status is %s", rule.Id, dsId, ds.Status)
continue
}
processor := process.NewProcessor(s.aconf.Heartbeat.EngineName, rule, dsId, s.alertRuleCache, s.targetCache, s.targetsOfAlertRuleCache, s.busiGroupCache, s.alertMuteCache, s.datasourceCache, s.ctx, s.stats)
externalRuleWorkers[processor.Key()] = processor
}
}
}
for hash, rule := range alertRuleWorkers {
if _, has := s.alertRules[hash]; !has {
rule.Prepare()
time.Sleep(time.Duration(20) * time.Millisecond)
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()
}
================================================
FILE: alert/eval/eval.go
================================================
package eval
import (
"context"
"encoding/json"
"errors"
"fmt"
"math"
"reflect"
"sort"
"strconv"
"strings"
"sync"
"text/template"
"time"
"github.com/ccfos/nightingale/v6/alert/astats"
"github.com/ccfos/nightingale/v6/alert/common"
"github.com/ccfos/nightingale/v6/alert/process"
"github.com/ccfos/nightingale/v6/dscache"
"github.com/ccfos/nightingale/v6/models"
"github.com/ccfos/nightingale/v6/pkg/ctx"
"github.com/ccfos/nightingale/v6/pkg/hash"
"github.com/ccfos/nightingale/v6/pkg/parser"
"github.com/ccfos/nightingale/v6/pkg/poster"
promsdk "github.com/ccfos/nightingale/v6/pkg/prom"
promql2 "github.com/ccfos/nightingale/v6/pkg/promql"
"github.com/ccfos/nightingale/v6/pkg/tplx"
"github.com/ccfos/nightingale/v6/pkg/unit"
"github.com/ccfos/nightingale/v6/prom"
"github.com/prometheus/common/model"
"github.com/robfig/cron/v3"
"github.com/toolkits/pkg/logger"
"github.com/toolkits/pkg/str"
)
type AlertRuleWorker struct {
DatasourceId int64
Quit chan struct{}
Inhibit bool
Rule *models.AlertRule
Processor *process.Processor
PromClients *prom.PromClientMap
Ctx *ctx.Context
Scheduler *cron.Cron
HostAndDeviceIdentCache sync.Map
LastSeriesStore map[uint64]models.DataResp
DeviceIdentHook func(arw *AlertRuleWorker, paramQuery models.ParamQuery) ([]string, error)
}
const (
GET_RULE_CONFIG = "get_rule_config"
GET_Processor = "get_Processor"
CHECK_QUERY = "check_query_config"
GET_CLIENT = "get_client"
QUERY_DATA = "query_data"
EXEC_TEMPLATE = "exec_template"
)
const (
JoinMark = "@@"
)
type JoinType string
const (
Left JoinType = "left"
Right JoinType = "right"
Inner JoinType = "inner"
)
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,
HostAndDeviceIdentCache: sync.Map{},
DeviceIdentHook: func(arw *AlertRuleWorker, paramQuery models.ParamQuery) ([]string, error) {
return nil, nil
},
LastSeriesStore: make(map[uint64]models.DataResp),
}
interval := rule.PromEvalInterval
if interval <= 0 {
interval = 10
}
if rule.CronPattern == "" {
rule.CronPattern = fmt.Sprintf("@every %ds", interval)
}
arw.Scheduler = cron.New(cron.WithSeconds(), cron.WithChain(cron.SkipIfStillRunning(cron.DefaultLogger)))
entryID, err := arw.Scheduler.AddFunc(rule.CronPattern, func() {
arw.Eval()
})
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d add cron pattern error: %v", arw.Rule.Id, arw.DatasourceId, err)
}
Processor.ScheduleEntry = arw.Scheduler.Entry(entryID)
Processor.PromEvalInterval = getPromEvalInterval(Processor.ScheduleEntry.Schedule)
return arw
}
func getPromEvalInterval(schedule cron.Schedule) int {
now := time.Now()
next1 := schedule.Next(now)
next2 := schedule.Next(next1)
return int(next2.Sub(next1).Seconds())
}
func (arw *AlertRuleWorker) Key() string {
return common.RuleKey(arw.DatasourceId, arw.Rule.Id)
}
func (arw *AlertRuleWorker) Hash() string {
return str.MD5(fmt.Sprintf("%d_%s_%s_%d",
arw.Rule.Id,
arw.Rule.CronPattern,
arw.Rule.RuleConfig,
arw.DatasourceId,
))
}
func (arw *AlertRuleWorker) Prepare() {
arw.Processor.RecoverAlertCurEventFromDb()
}
func (arw *AlertRuleWorker) Start() {
arw.Scheduler.Start()
}
func (arw *AlertRuleWorker) Eval() {
begin := time.Now()
var message string
defer func() {
if len(message) == 0 {
logger.Infof("alert_eval_%d datasource_%d finished, duration:%v", arw.Rule.Id, arw.DatasourceId, time.Since(begin))
} else {
logger.Warningf("alert_eval_%d datasource_%d finished, duration:%v, message:%s", arw.Rule.Id, arw.DatasourceId, time.Since(begin), message)
}
}()
if arw.Processor.PromEvalInterval == 0 {
arw.Processor.PromEvalInterval = getPromEvalInterval(arw.Processor.ScheduleEntry.Schedule)
}
cachedRule := arw.Rule
if cachedRule == nil {
message = "rule not found"
return
}
arw.Processor.Stats.CounterRuleEval.WithLabelValues().Inc()
arw.HostAndDeviceIdentCache = sync.Map{}
typ := cachedRule.GetRuleType()
var (
anomalyPoints []models.AnomalyPoint
recoverPoints []models.AnomalyPoint
err error
)
switch typ {
case models.PROMETHEUS:
anomalyPoints, err = arw.GetPromAnomalyPoint(cachedRule.RuleConfig)
case models.HOST:
anomalyPoints, err = arw.GetHostAnomalyPoint(cachedRule.RuleConfig)
case models.LOKI:
anomalyPoints, err = arw.GetPromAnomalyPoint(cachedRule.RuleConfig)
default:
anomalyPoints, recoverPoints, err = arw.GetAnomalyPoint(cachedRule, arw.Processor.DatasourceId())
}
if err != nil {
message = fmt.Sprintf("failed to get anomaly points: %v", err)
return
}
if arw.Processor == nil {
message = "processor is nil"
return
}
if arw.Inhibit {
pointsMap := make(map[string]models.AnomalyPoint)
for _, point := range recoverPoints {
// 对于恢复的事件,合并处理
tagHash := process.TagHash(point)
p, exists := pointsMap[tagHash]
if !exists {
pointsMap[tagHash] = point
continue
}
if p.Severity > point.Severity {
hash := process.Hash(cachedRule.Id, arw.Processor.DatasourceId(), p)
arw.Processor.DeleteProcessEvent(hash)
models.AlertCurEventDelByHash(arw.Ctx, hash)
pointsMap[tagHash] = point
}
}
now := time.Now().Unix()
for _, point := range pointsMap {
str := fmt.Sprintf("%v", point.Value)
arw.Processor.RecoverSingle(true, process.Hash(cachedRule.Id, arw.Processor.DatasourceId(), point), now, &str)
}
} else {
now := time.Now().Unix()
for _, point := range recoverPoints {
str := fmt.Sprintf("%v", point.Value)
arw.Processor.RecoverSingle(true, process.Hash(cachedRule.Id, arw.Processor.DatasourceId(), point), now, &str)
}
}
arw.Processor.Handle(anomalyPoints, "inner", arw.Inhibit)
}
func (arw *AlertRuleWorker) Stop() {
logger.Infof("alert_eval_%d datasource_%d stopped", arw.Rule.Id, arw.DatasourceId)
close(arw.Quit)
c := arw.Scheduler.Stop()
<-c.Done()
}
func (arw *AlertRuleWorker) GetPromAnomalyPoint(ruleConfig string) ([]models.AnomalyPoint, error) {
var lst []models.AnomalyPoint
start := time.Now()
defer func() {
arw.Processor.Stats.GaugeRuleEvalDuration.WithLabelValues(fmt.Sprintf("%v", arw.Rule.Id), fmt.Sprintf("%v", arw.Processor.DatasourceId())).Set(float64(time.Since(start).Milliseconds()))
}()
var rule *models.PromRuleConfig
if err := json.Unmarshal([]byte(ruleConfig), &rule); err != nil {
logger.Errorf("alert_eval_%d datasource_%d rule_config:%s, error:%v", arw.Rule.Id, arw.DatasourceId, ruleConfig, err)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_RULE_CONFIG, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
return lst, err
}
if rule == nil {
logger.Errorf("alert_eval_%d datasource_%d rule_config:%s, error:rule is nil", arw.Rule.Id, arw.DatasourceId, ruleConfig)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_RULE_CONFIG, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
return lst, errors.New("rule is nil")
}
arw.Inhibit = rule.Inhibit
for i, query := range rule.Queries {
readerClient := arw.PromClients.GetCli(arw.DatasourceId)
if readerClient == nil {
logger.Warningf("alert_eval_%d datasource_%d error reader client is nil", arw.Rule.Id, arw.DatasourceId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_CLIENT, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(-2)
continue
}
if query.VarEnabled && strings.Contains(query.PromQl, "$") {
var anomalyPoints []models.AnomalyPoint
if hasLabelLossAggregator(query) || notExactMatch(query) {
// 若有聚合函数或非精确匹配则需要先填充变量然后查询,这个方式效率较低
anomalyPoints = arw.VarFillingBeforeQuery(query, readerClient)
arw.Processor.Stats.CounterVarFillingQuery.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
"BeforeQuery",
).Inc()
} else {
// 先查询再过滤变量,效率较高,但无法处理有聚合函数的情况
anomalyPoints = arw.VarFillingAfterQuery(query, readerClient)
arw.Processor.Stats.CounterVarFillingQuery.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
"AfterQuery",
).Inc()
}
lst = append(lst, anomalyPoints...)
} else {
// 无变量
promql := strings.TrimSpace(query.PromQl)
if promql == "" {
logger.Warningf("alert_eval_%d datasource_%d promql is blank", arw.Rule.Id, arw.DatasourceId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), CHECK_QUERY, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
continue
}
if arw.PromClients.IsNil(arw.DatasourceId) {
logger.Warningf("alert_eval_%d datasource_%d error reader client is nil", arw.Rule.Id, arw.DatasourceId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_CLIENT, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
continue
}
var warnings promsdk.Warnings
arw.Processor.Stats.CounterQueryDataTotal.WithLabelValues(fmt.Sprintf("%d", arw.DatasourceId), fmt.Sprintf("%d", arw.Rule.Id)).Inc()
value, warnings, err := readerClient.Query(context.Background(), promql, time.Now())
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d promql:%s, error:%v", arw.Rule.Id, arw.DatasourceId, promql, err)
arw.Processor.Stats.CounterQueryDataErrorTotal.WithLabelValues(fmt.Sprintf("%d", arw.DatasourceId)).Inc()
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), QUERY_DATA, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(-1)
return lst, err
}
if len(warnings) > 0 {
logger.Errorf("alert_eval_%d datasource_%d promql:%s, warnings:%v", arw.Rule.Id, arw.DatasourceId, promql, warnings)
arw.Processor.Stats.CounterQueryDataErrorTotal.WithLabelValues(fmt.Sprintf("%d", arw.DatasourceId)).Inc()
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), QUERY_DATA, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
}
logger.Infof("alert_eval_%d datasource_%d query:%+v, value:%v", arw.Rule.Id, arw.DatasourceId, query, value)
points := models.ConvertAnomalyPoints(value)
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(float64(len(points)))
for i := 0; i < len(points); i++ {
points[i].Severity = query.Severity
points[i].Query = promql
points[i].ValuesUnit = map[string]unit.FormattedValue{
"v": unit.ValueFormatter(query.Unit, 2, points[i].Value),
}
}
lst = append(lst, points...)
}
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(float64(len(lst)))
}
return lst, nil
}
type sample struct {
Metric model.Metric `json:"metric"`
Value model.SampleValue `json:"value"`
Timestamp model.Time
}
// VarFillingAfterQuery 填充变量,先查询再填充变量
// 公式: mem_used_percent{host="$host"} > $val 其中 $host 为参数变量,$val 为值变量
// 实现步骤:
// 依次遍历参数配置节点,保证同一参数变量的子筛选可以覆盖上一层筛选
// 每个节点先查询无参数的 query, 即 mem_used_percent{} > curVal, 得到满足值变量的所有结果
// 结果中有满足本节点参数变量的值,加入异常点列表
// 参数变量的值不满足的组合,需要覆盖上层筛选中产生的异常点
func (arw *AlertRuleWorker) VarFillingAfterQuery(query models.PromQuery, readerClient promsdk.API) []models.AnomalyPoint {
varToLabel := ExtractVarMapping(query.PromQl)
fullQuery := removeVal(query.PromQl)
// 存储所有的异常点,key 为参数变量的组合,可以实现子筛选对上一层筛选的覆盖
anomalyPointsMap := make(map[string]models.AnomalyPoint)
// 统一变量配置格式
VarConfigForCalc := &models.ChildVarConfig{
ParamVal: make([]map[string]models.ParamQuery, 1),
ChildVarConfigs: query.VarConfig.ChildVarConfigs,
}
VarConfigForCalc.ParamVal[0] = make(map[string]models.ParamQuery)
for _, p := range query.VarConfig.ParamVal {
VarConfigForCalc.ParamVal[0][p.Name] = models.ParamQuery{
ParamType: p.ParamType,
Query: p.Query,
}
}
// 使用一个统一的参数变量顺序
var ParamKeys []string
for val, valQuery := range VarConfigForCalc.ParamVal[0] {
if valQuery.ParamType == "threshold" {
continue
}
ParamKeys = append(ParamKeys, val)
}
sort.Slice(ParamKeys, func(i, j int) bool {
return ParamKeys[i] < ParamKeys[j]
})
// 遍历变量配置链表
curNode := VarConfigForCalc
for curNode != nil {
for _, param := range curNode.ParamVal {
// curQuery 当前节点的无参数 query,用于时序库查询
curQuery := fullQuery
// realQuery 当前节点产生异常点的 query,用于告警展示
realQuery := query.PromQl
// 取出阈值变量
valMap := make(map[string]string)
for val, valQuery := range param {
if valQuery.ParamType == "threshold" {
valMap[val] = getString(valQuery.Query)
}
}
// 替换值变量
for key, val := range valMap {
curQuery = strings.Replace(curQuery, fmt.Sprintf("$%s", key), val, -1)
realQuery = strings.Replace(realQuery, fmt.Sprintf("$%s", key), val, -1)
}
// 得到满足值变量的所有结果
arw.Processor.Stats.CounterQueryDataTotal.WithLabelValues(fmt.Sprintf("%d", arw.DatasourceId), fmt.Sprintf("%d", arw.Rule.Id)).Inc()
value, _, err := readerClient.Query(context.Background(), curQuery, time.Now())
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d promql:%s, error:%v", arw.Rule.Id, arw.DatasourceId, curQuery, err)
continue
}
seqVals := getSamples(value)
// 得到参数变量的所有组合
paramPermutation, err := arw.getParamPermutation(param, ParamKeys, varToLabel, query.PromQl, readerClient)
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d paramPermutation error:%v", arw.Rule.Id, arw.DatasourceId, err)
continue
}
// 判断哪些参数值符合条件
for i := range seqVals {
curRealQuery := realQuery
var cur []string
for _, paramKey := range ParamKeys {
val := string(seqVals[i].Metric[model.LabelName(varToLabel[paramKey])])
cur = append(cur, val)
curRealQuery = fillVar(curRealQuery, paramKey, val)
}
if _, ok := paramPermutation[strings.Join(cur, JoinMark)]; ok {
anomalyPointsMap[strings.Join(cur, JoinMark)] = models.AnomalyPoint{
Key: seqVals[i].Metric.String(),
Timestamp: seqVals[i].Timestamp.Unix(),
Value: float64(seqVals[i].Value),
Labels: seqVals[i].Metric,
Severity: query.Severity,
Query: curRealQuery,
}
// 生成异常点后,删除该参数组合
delete(paramPermutation, strings.Join(cur, JoinMark))
}
}
// 剩余的参数组合为本层筛选不产生异常点的组合,需要覆盖上层筛选中产生的异常点
for k, _ := range paramPermutation {
delete(anomalyPointsMap, k)
}
}
curNode = curNode.ChildVarConfigs
}
anomalyPoints := make([]models.AnomalyPoint, 0)
for _, point := range anomalyPointsMap {
anomalyPoints = append(anomalyPoints, point)
}
return anomalyPoints
}
// getSamples 获取查询结果的所有样本,并转化为统一的格式
func getSamples(value model.Value) []sample {
var seqVals []sample
switch value.Type() {
case model.ValVector:
items, ok := value.(model.Vector)
if !ok {
break
}
for i := range items {
seqVals = append(seqVals, sample{
Metric: items[i].Metric,
Value: items[i].Value,
Timestamp: items[i].Timestamp,
})
}
case model.ValMatrix:
items, ok := value.(model.Matrix)
if !ok {
break
}
for i := range items {
last := items[i].Values[len(items[i].Values)-1]
seqVals = append(seqVals, sample{
Metric: items[i].Metric,
Value: last.Value,
Timestamp: last.Timestamp,
})
}
default:
}
return seqVals
}
// removeVal 去除 promql 中的参数变量
// mem{test1=\"$test1\",test2=\"test2\"} > $val1 and mem{test3=\"test3\",test4=\"$test4\"} > $val2
// ==> mem{test2=\"test2\"} > $val1 and mem{test3=\"test3\"} > $val2
func removeVal(promql string) string {
sb := strings.Builder{}
n := len(promql)
start := false
lastIdx := 0
curIdx := 0
isVar := false
for curIdx < n {
if !start {
if promql[curIdx] == '{' {
start = true
lastIdx = curIdx
}
sb.WriteRune(rune(promql[curIdx]))
} else {
if promql[curIdx] == '$' {
isVar = true
}
if promql[curIdx] == ',' || promql[curIdx] == '}' {
if !isVar {
if sb.String()[sb.Len()-1] == '{' {
lastIdx++
}
sb.WriteString(promql[lastIdx:curIdx])
}
isVar = false
if promql[curIdx] == '}' {
start = false
sb.WriteRune(rune(promql[curIdx]))
}
lastIdx = curIdx
}
}
curIdx++
}
return sb.String()
}
// 获取参数变量的所有组合
func (arw *AlertRuleWorker) getParamPermutation(paramVal map[string]models.ParamQuery, paramKeys []string, varToLabel map[string]string, originPromql string, readerClient promsdk.API) (map[string]struct{}, error) {
// 参数变量查询,得到参数变量值
paramMap := make(map[string][]string)
for _, paramKey := range paramKeys {
var params []string
paramQuery, ok := paramVal[paramKey]
if !ok {
return nil, fmt.Errorf("param key not found: %s", paramKey)
}
switch paramQuery.ParamType {
case "host":
hostIdents, err := arw.getHostIdents(paramQuery)
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d fail to get host idents, error:%v", arw.Rule.Id, arw.DatasourceId, err)
break
}
params = hostIdents
case "device":
deviceIdents, err := arw.getDeviceIdents(paramQuery)
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d fail to get device idents, error:%v", arw.Rule.Id, arw.DatasourceId, err)
break
}
params = deviceIdents
case "enum":
q, _ := json.Marshal(paramQuery.Query)
var query []string
err := json.Unmarshal(q, &query)
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d query:%s fail to unmarshalling into string slice, error:%v", arw.Rule.Id, arw.DatasourceId, paramQuery.Query, err)
}
if len(query) == 0 {
paramsKeyAllLabel, err := getParamKeyAllLabel(varToLabel[paramKey], originPromql, readerClient, arw.DatasourceId, arw.Rule.Id, arw.Processor.Stats)
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d fail to getParamKeyAllLabel, error:%v query:%s", arw.Rule.Id, arw.DatasourceId, err, paramQuery.Query)
}
params = paramsKeyAllLabel
} else {
params = query
}
default:
return nil, fmt.Errorf("unknown param type: %s", paramQuery.ParamType)
}
if len(params) == 0 {
return nil, fmt.Errorf("param key: %s, params is empty", paramKey)
}
logger.Infof("alert_eval_%d datasource_%d paramKey: %s, params: %v", arw.Rule.Id, arw.DatasourceId, paramKey, params)
paramMap[paramKey] = params
}
// 得到以 paramKeys 为顺序的所有参数组合
permutation := mapPermutation(paramKeys, paramMap)
res := make(map[string]struct{})
for i := range permutation {
res[strings.Join(permutation[i], JoinMark)] = struct{}{}
}
return res, nil
}
func getParamKeyAllLabel(paramKey string, promql string, client promsdk.API, dsId int64, rid int64, stats *astats.Stats) ([]string, error) {
labels, metricName, err := promql2.GetLabelsAndMetricNameWithReplace(promql, "$")
if err != nil {
return nil, fmt.Errorf("promql:%s, get labels error:%v", promql, err)
}
labelstrs := make([]string, 0)
for _, label := range labels {
if strings.HasPrefix(label.Value, "$") {
continue
}
labelstrs = append(labelstrs, label.Name+label.Op+label.Value)
}
pr := metricName + "{" + strings.Join(labelstrs, ",") + "}"
stats.CounterQueryDataTotal.WithLabelValues(fmt.Sprintf("%d", dsId), fmt.Sprintf("%d", rid)).Inc()
value, _, err := client.Query(context.Background(), pr, time.Now())
if err != nil {
return nil, fmt.Errorf("promql: %s query error: %v", pr, err)
}
labelValuesMap := make(map[string]struct{})
switch value.Type() {
case model.ValVector:
vector := value.(model.Vector)
for _, sample := range vector {
for labelName, labelValue := range sample.Metric {
// 只处理ParamKeys中指定的label
if string(labelName) == paramKey {
labelValuesMap[string(labelValue)] = struct{}{}
}
}
}
case model.ValMatrix:
matrix := value.(model.Matrix)
for _, series := range matrix {
for labelName, labelValue := range series.Metric {
// 只处理ParamKeys中指定的label
if string(labelName) == paramKey {
labelValuesMap[string(labelValue)] = struct{}{}
}
}
}
}
result := make([]string, 0)
for labelValue, _ := range labelValuesMap {
result = append(result, labelValue)
}
return result, nil
}
func (arw *AlertRuleWorker) getHostIdents(paramQuery models.ParamQuery) ([]string, error) {
var params []string
q, _ := json.Marshal(paramQuery.Query)
cacheKey := "Host_" + string(q)
value, hit := arw.HostAndDeviceIdentCache.Load(cacheKey)
if idents, ok := value.([]string); hit && ok {
params = idents
return params, nil
}
var queries []models.HostQuery
err := json.Unmarshal(q, &queries)
if err != nil {
return nil, err
}
if !arw.Ctx.IsCenter {
lst, err := poster.PostByUrlsWithResp[[]*models.Target](arw.Ctx, "/v1/n9e/targets-of-host-query", queries)
if err != nil {
return nil, err
}
for i := range lst {
params = append(params, lst[i].Ident)
}
} else {
hostsQuery := models.GetHostsQuery(queries)
session := models.TargetFilterQueryBuild(arw.Ctx, hostsQuery, 0, 0)
var lst []*models.Target
err = session.Find(&lst).Error
if err != nil {
return nil, err
}
for i := range lst {
params = append(params, lst[i].Ident)
}
}
arw.HostAndDeviceIdentCache.Store(cacheKey, params)
return params, nil
}
func (arw *AlertRuleWorker) getDeviceIdents(paramQuery models.ParamQuery) ([]string, error) {
return arw.DeviceIdentHook(arw, paramQuery)
}
// 生成所有排列组合
func mapPermutation(paramKeys []string, paraMap map[string][]string) [][]string {
var result [][]string
current := make([]string, len(paramKeys))
combine(paramKeys, paraMap, 0, current, &result)
return result
}
// 递归生成所有排列组合
func combine(paramKeys []string, paraMap map[string][]string, index int, current []string, result *[][]string) {
// 当到达最后一个 key 时,存储当前的组合
if index == len(paramKeys) {
combination := make([]string, len(current))
copy(combination, current)
*result = append(*result, combination)
return
}
// 获取当前 key 对应的 value 列表
key := paramKeys[index]
valueList := paraMap[key]
// 遍历每个 value,并递归生成下一个 key 的组合
for _, value := range valueList {
current[index] = value
combine(paramKeys, paraMap, index+1, current, result)
}
}
func (arw *AlertRuleWorker) GetHostAnomalyPoint(ruleConfig string) ([]models.AnomalyPoint, error) {
var lst []models.AnomalyPoint
start := time.Now()
defer func() {
arw.Processor.Stats.GaugeRuleEvalDuration.WithLabelValues(fmt.Sprintf("%v", arw.Rule.Id), fmt.Sprintf("%v", arw.Processor.DatasourceId())).Set(float64(time.Since(start).Milliseconds()))
}()
var rule *models.HostRuleConfig
if err := json.Unmarshal([]byte(ruleConfig), &rule); err != nil {
logger.Errorf("alert_eval_%d datasource_%d rule_config:%s, error:%v", arw.Rule.Id, arw.DatasourceId, ruleConfig, err)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_RULE_CONFIG, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
return lst, err
}
if rule == nil {
logger.Errorf("alert_eval_%d datasource_%d rule_config:%s, error:rule is nil", arw.Rule.Id, arw.DatasourceId, ruleConfig)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_RULE_CONFIG, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
return lst, errors.New("rule is nil")
}
arw.Inhibit = rule.Inhibit
now := time.Now().Unix()
for _, trigger := range rule.Triggers {
switch trigger.Type {
case "target_miss":
t := now - int64(trigger.Duration)
var idents, engineIdents, missEngineIdents []string
var exists bool
if arw.Ctx.IsCenter {
// 如果是中心节点, 将不再上报数据的主机 engineName 为空的机器,也加入到 targets 中
missEngineIdents, exists = arw.Processor.TargetsOfAlertRuleCache.Get("", arw.Rule.Id)
if !exists {
logger.Debugf("alert_eval_%d datasource_%d targets not found engineName:%s", arw.Rule.Id, arw.DatasourceId, arw.Processor.EngineName)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), QUERY_DATA, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
}
}
idents = append(idents, missEngineIdents...)
engineIdents, exists = arw.Processor.TargetsOfAlertRuleCache.Get(arw.Processor.EngineName, arw.Rule.Id)
if !exists {
logger.Warningf("alert_eval_%d datasource_%d targets not found engineName:%s", arw.Rule.Id, arw.DatasourceId, arw.Processor.EngineName)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), QUERY_DATA, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
}
idents = append(idents, engineIdents...)
if len(idents) == 0 {
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
continue
}
var missTargets []string
targetUpdateTimeMap := arw.Processor.TargetCache.GetHostUpdateTime(idents)
for ident, updateTime := range targetUpdateTimeMap {
if updateTime < t {
missTargets = append(missTargets, ident)
}
}
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(float64(len(missTargets)))
logger.Debugf("alert_eval_%d datasource_%d missTargets:%v", arw.Rule.Id, arw.DatasourceId, missTargets)
targets := arw.Processor.TargetCache.Gets(missTargets)
for _, target := range targets {
m := make(map[string]string)
for k, v := range target.TagsMap {
m[k] = v
}
m["ident"] = target.Ident
lst = append(lst, models.NewAnomalyPoint(trigger.Type, m, now, float64(now-target.BeatTime), trigger.Severity))
}
case "offset":
idents, exists := arw.Processor.TargetsOfAlertRuleCache.Get(arw.Processor.EngineName, arw.Rule.Id)
if !exists {
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
logger.Warningf("alert_eval_%d datasource_%d targets not found", arw.Rule.Id, arw.DatasourceId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), QUERY_DATA, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
continue
}
targets := arw.Processor.TargetCache.Gets(idents)
targetMap := make(map[string]*models.Target)
for _, target := range targets {
targetMap[target.Ident] = target
}
offsetIdents := make(map[string]int64)
targetsMeta := arw.Processor.TargetCache.GetHostMetas(targets)
for ident, meta := range targetsMeta {
if meta.CpuNum <= 0 {
// means this target is not collect by categraf, do not check offset
continue
}
if target, exists := targetMap[ident]; exists {
if now-target.BeatTime > 120 {
// means this target is not a active host, do not check offset
continue
}
}
offset := meta.Offset
if math.Abs(float64(offset)) > float64(trigger.Duration) {
offsetIdents[ident] = offset
}
}
logger.Debugf("alert_eval_%d datasource_%d offsetIdents:%v", arw.Rule.Id, arw.DatasourceId, offsetIdents)
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(float64(len(offsetIdents)))
for host, offset := range offsetIdents {
m := make(map[string]string)
target, exists := arw.Processor.TargetCache.Get(host)
if exists {
for k, v := range target.TagsMap {
m[k] = v
}
}
m["ident"] = host
lst = append(lst, models.NewAnomalyPoint(trigger.Type, m, now, float64(offset), trigger.Severity))
}
case "pct_target_miss":
t := now - int64(trigger.Duration)
idents, exists := arw.Processor.TargetsOfAlertRuleCache.Get(arw.Processor.EngineName, arw.Rule.Id)
if !exists {
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
logger.Warningf("alert_eval_%d datasource_%d targets not found", arw.Rule.Id, arw.DatasourceId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), QUERY_DATA, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
continue
}
var missTargets []string
targetUpdateTimeMap := arw.Processor.TargetCache.GetHostUpdateTime(idents)
for ident, updateTime := range targetUpdateTimeMap {
if updateTime < t {
missTargets = append(missTargets, ident)
}
}
logger.Debugf("alert_eval_%d datasource_%d missTargets:%v", arw.Rule.Id, arw.DatasourceId, missTargets)
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(float64(len(missTargets)))
pct := float64(len(missTargets)) / float64(len(idents)) * 100
if pct >= float64(trigger.Percent) {
lst = append(lst, models.NewAnomalyPoint(trigger.Type, nil, now, pct, trigger.Severity))
}
}
}
return lst, nil
}
func flatten(rehashed map[uint64][][]uint64) map[uint64][]uint64 {
seriesTagIndex := make(map[uint64][]uint64)
var i uint64
for _, HashTagIndex := range rehashed {
for u := range HashTagIndex {
seriesTagIndex[i] = HashTagIndex[u]
i++
}
}
return seriesTagIndex
}
// onJoin 组合两个经过 rehash 之后的集合
// 如查询 A,经过 on data_base rehash 分组后
// [[A1{data_base=1, table=alert},A2{data_base=1, table=alert}],[A5{data_base=1, table=board}]]
// [[A3{data_base=2, table=board}],[A4{data_base=2, table=alert}]]
// 查询 B,经过 on data_base rehash 分组后
// [[B1{data_base=1, table=alert}]]
// [[B2{data_base=2, table=alert}]]
// 内联得到
// [[A1{data_base=1, table=alert},A2{data_base=1, table=alert},B1{data_base=1, table=alert}],[A5{data_base=1, table=board},[B1{data_base=1, table=alert}]]
// [[A3{data_base=2, table=board},B2{data_base=2, table=alert}],[A4{data_base=2, table=alert},B2{data_base=2, table=alert}]]
func onJoin(reHashTagIndex1 map[uint64][][]uint64, reHashTagIndex2 map[uint64][][]uint64, joinType JoinType) map[uint64][][]uint64 {
reHashTagIndex := make(map[uint64][][]uint64)
for rehash := range reHashTagIndex1 {
if _, ok := reHashTagIndex2[rehash]; ok {
// 若有 rehash 相同的记录,两两合并
for i1 := range reHashTagIndex1[rehash] {
for i2 := range reHashTagIndex2[rehash] {
reHashTagIndex[rehash] = append(reHashTagIndex[rehash], mergeNewArray(reHashTagIndex1[rehash][i1], reHashTagIndex2[rehash][i2]))
}
}
} else {
// 合并方式不为 inner 时,需要保留 reHashTagIndex1 中未匹配的记录
if joinType != Inner {
reHashTagIndex[rehash] = reHashTagIndex1[rehash]
}
}
}
return reHashTagIndex
}
// rehashSet 重新 hash 分组
// 如当前查询 A 有五条记录
// A1{data_base=1, table=alert}
// A2{data_base=1, table=alert}
// A3{data_base=2, table=board}
// A4{data_base=2, table=alert}
// A5{data_base=1, table=board}
// 经过预处理(按曲线分组,此步已在进入 GetAnomalyPoint 函数前完成)后,分为 4 组,
// [A1{data_base=1, table=alert},A2{data_base=1, table=alert}]
// [A3{data_base=2, table=board}]
// [A4{data_base=2, table=alert}]
// [A5{data_base=1, table=board}]
// 若 rehashSet 按 data_base 重新分组,此时会得到按 rehash 值分的二维数组,即不会将 rehash 值相同的记录完全合并
// [[A1{data_base=1, table=alert},A2{data_base=1, table=alert}],[A5{data_base=1, table=board}]]
// [[A3{data_base=2, table=board}],[A4{data_base=2, table=alert}]]
func rehashSet(seriesTagIndex1 map[uint64][]uint64, seriesStore map[uint64]models.DataResp, on []string) map[uint64][][]uint64 {
reHashTagIndex := make(map[uint64][][]uint64)
for _, seriesHashes := range seriesTagIndex1 {
if len(seriesHashes) == 0 {
continue
}
series, exists := seriesStore[seriesHashes[0]]
if !exists {
continue
}
rehash := hash.GetTargetTagHash(series.Metric, on)
if _, ok := reHashTagIndex[rehash]; !ok {
reHashTagIndex[rehash] = make([][]uint64, 0)
}
reHashTagIndex[rehash] = append(reHashTagIndex[rehash], seriesHashes)
}
return reHashTagIndex
}
// 笛卡尔积,查询的结果两两合并
func cartesianJoin(seriesTagIndex1 map[uint64][]uint64, seriesTagIndex2 map[uint64][]uint64) map[uint64][]uint64 {
var index uint64
seriesTagIndex := make(map[uint64][]uint64)
for _, seriesHashes1 := range seriesTagIndex1 {
for _, seriesHashes2 := range seriesTagIndex2 {
seriesTagIndex[index] = mergeNewArray(seriesHashes1, seriesHashes2)
index++
}
}
return seriesTagIndex
}
// noneJoin 直接拼接
func noneJoin(seriesTagIndex1 map[uint64][]uint64, seriesTagIndex2 map[uint64][]uint64) map[uint64][]uint64 {
seriesTagIndex := make(map[uint64][]uint64)
var index uint64
for _, seriesHashes := range seriesTagIndex1 {
seriesTagIndex[index] = seriesHashes
index++
}
for _, seriesHashes := range seriesTagIndex2 {
seriesTagIndex[index] = seriesHashes
index++
}
return seriesTagIndex
}
// originalJoin 原始分组方案,key 相同,即标签全部相同分为一组
func originalJoin(seriesTagIndex1 map[uint64][]uint64, seriesTagIndex2 map[uint64][]uint64) map[uint64][]uint64 {
seriesTagIndex := make(map[uint64][]uint64)
for tagHash, seriesHashes := range seriesTagIndex1 {
if _, ok := seriesTagIndex[tagHash]; !ok {
seriesTagIndex[tagHash] = mergeNewArray(seriesHashes)
} else {
seriesTagIndex[tagHash] = append(seriesTagIndex[tagHash], seriesHashes...)
}
}
for tagHash, seriesHashes := range seriesTagIndex2 {
if _, ok := seriesTagIndex[tagHash]; !ok {
seriesTagIndex[tagHash] = mergeNewArray(seriesHashes)
} else {
seriesTagIndex[tagHash] = append(seriesTagIndex[tagHash], seriesHashes...)
}
}
return seriesTagIndex
}
// exclude 左斥,留下在 reHashTagIndex1 中,但不在 reHashTagIndex2 中的记录
func exclude(reHashTagIndex1 map[uint64][][]uint64, reHashTagIndex2 map[uint64][][]uint64) map[uint64][][]uint64 {
reHashTagIndex := make(map[uint64][][]uint64)
for rehash, _ := range reHashTagIndex1 {
if _, ok := reHashTagIndex2[rehash]; !ok {
reHashTagIndex[rehash] = reHashTagIndex1[rehash]
}
}
return reHashTagIndex
}
func MakeSeriesMap(series []models.DataResp, seriesTagIndex map[uint64][]uint64, seriesStore map[uint64]models.DataResp) {
for i := 0; i < len(series); i++ {
seriesHash := hash.GetHash(series[i].Metric, series[i].Ref)
tagHash := hash.GetTagHash(series[i].Metric)
seriesStore[seriesHash] = series[i]
// 将曲线按照相同的 tag 分组
if _, exists := seriesTagIndex[tagHash]; !exists {
seriesTagIndex[tagHash] = make([]uint64, 0)
}
seriesTagIndex[tagHash] = append(seriesTagIndex[tagHash], seriesHash)
}
}
func mergeNewArray(arg ...[]uint64) []uint64 {
res := make([]uint64, 0)
for _, a := range arg {
res = append(res, a...)
}
return res
}
func ProcessJoins(ruleId int64, trigger models.Trigger, seriesTagIndexes map[string]map[uint64][]uint64, seriesStore map[uint64]models.DataResp) map[uint64][]uint64 {
last := make(map[uint64][]uint64)
if len(seriesTagIndexes) == 0 {
return last
}
if len(trigger.Joins) == 0 {
idx := 0
for _, seriesTagIndex := range seriesTagIndexes {
if idx == 0 {
last = seriesTagIndex
} else {
last = originalJoin(last, seriesTagIndex)
}
idx++
}
return last
}
// 有 join 条件,按条件依次合并
if len(seriesTagIndexes) < len(trigger.Joins)+1 {
logger.Errorf("alert_eval_%d queries' count: %d not match join condition's count: %d", ruleId, len(seriesTagIndexes), len(trigger.Joins))
return nil
}
last = seriesTagIndexes[trigger.JoinRef]
lastRehashed := rehashSet(last, seriesStore, trigger.Joins[0].On)
for i := range trigger.Joins {
cur := seriesTagIndexes[trigger.Joins[i].Ref]
switch trigger.Joins[i].JoinType {
case "original":
last = originalJoin(last, cur)
case "none":
last = noneJoin(last, cur)
case "cartesian":
last = cartesianJoin(last, cur)
case "inner_join":
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
lastRehashed = onJoin(lastRehashed, curRehashed, Inner)
last = flatten(lastRehashed)
case "left_join":
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
lastRehashed = onJoin(lastRehashed, curRehashed, Left)
last = flatten(lastRehashed)
case "right_join":
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
lastRehashed = onJoin(curRehashed, lastRehashed, Right)
last = flatten(lastRehashed)
case "left_exclude":
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
lastRehashed = exclude(lastRehashed, curRehashed)
last = flatten(lastRehashed)
case "right_exclude":
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
lastRehashed = exclude(curRehashed, lastRehashed)
last = flatten(lastRehashed)
default:
logger.Warningf("alert_eval_%d join type:%s not support", ruleId, trigger.Joins[i].JoinType)
}
}
return last
}
func GetQueryRef(query interface{}) (string, error) {
// 首先检查是否为 map
if m, ok := query.(map[string]interface{}); ok {
if ref, exists := m["ref"]; exists {
if refStr, ok := ref.(string); ok {
return refStr, nil
}
return "", fmt.Errorf("ref 字段不是字符串类型")
}
return "", fmt.Errorf("query 中没有找到 ref 字段")
}
// 如果不是 map,则按原来的方式处理结构体
v := reflect.ValueOf(query)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return "", fmt.Errorf("query not a struct or map")
}
refField := v.FieldByName("Ref")
if !refField.IsValid() {
return "", fmt.Errorf("not find ref field")
}
if refField.Kind() != reflect.String {
return "", fmt.Errorf("ref not a string")
}
return refField.String(), nil
}
// query 可能是 string 或是 int int64 float64 等数字,全部转为 string
func getString(query interface{}) string {
switch query.(type) {
case string:
return query.(string)
case float64:
return strconv.FormatFloat(query.(float64), 'f', -1, 64)
default:
return ""
}
}
func GetQueryRefAndUnit(query interface{}) (string, string, error) {
type Query struct {
Ref string `json:"ref"`
Unit string `json:"unit"`
}
queryMap := Query{}
queryBytes, err := json.Marshal(query)
if err != nil {
return "", "", err
}
json.Unmarshal(queryBytes, &queryMap)
return queryMap.Ref, queryMap.Unit, nil
}
// VarFillingBeforeQuery 填充变量,先填充变量再查询,针对有聚合函数的情况
// 公式: avg(mem_used_percent{host="$host"}) > $val 其中 $host 为参数变量,$val 为值变量
// 实现步骤:
// 依次遍历参数配置节点,保证同一参数变量的子筛选可以覆盖上一层筛选
// 每个节点先填充参数再进行查询, 即先得到完整的 promql avg(mem_used_percent{host="127.0.0.1"}) > 5
// 再查询得到满足值变量的所有结果加入异常点列表
// 参数变量的值不满足的组合,需要覆盖上层筛选中产生的异常点
func (arw *AlertRuleWorker) VarFillingBeforeQuery(query models.PromQuery, readerClient promsdk.API) []models.AnomalyPoint {
varToLabel := ExtractVarMapping(query.PromQl)
// 存储异常点的 map,key 为参数变量的组合,可以实现子筛选对上一层筛选的覆盖
anomalyPointsMap := sync.Map{}
// 统一变量配置格式
VarConfigForCalc := &models.ChildVarConfig{
ParamVal: make([]map[string]models.ParamQuery, 1),
ChildVarConfigs: query.VarConfig.ChildVarConfigs,
}
VarConfigForCalc.ParamVal[0] = make(map[string]models.ParamQuery)
for _, p := range query.VarConfig.ParamVal {
VarConfigForCalc.ParamVal[0][p.Name] = models.ParamQuery{
ParamType: p.ParamType,
Query: p.Query,
}
}
// 使用一个统一的参数变量顺序
var ParamKeys []string
for val, valQuery := range VarConfigForCalc.ParamVal[0] {
if valQuery.ParamType == "threshold" {
continue
}
ParamKeys = append(ParamKeys, val)
}
sort.Slice(ParamKeys, func(i, j int) bool {
return ParamKeys[i] < ParamKeys[j]
})
// 遍历变量配置链表
curNode := VarConfigForCalc
for curNode != nil {
for _, param := range curNode.ParamVal {
curPromql := query.PromQl
// 取出阈值变量
valMap := make(map[string]string)
for val, valQuery := range param {
if valQuery.ParamType == "threshold" {
valMap[val] = getString(valQuery.Query)
}
}
// 替换阈值变量
for key, val := range valMap {
curPromql = strings.Replace(curPromql, fmt.Sprintf("$%s", key), val, -1)
}
// 得到参数变量的所有组合
paramPermutation, err := arw.getParamPermutation(param, ParamKeys, varToLabel, query.PromQl, readerClient)
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d paramPermutation error:%v", arw.Rule.Id, arw.DatasourceId, err)
continue
}
keyToPromql := make(map[string]string)
for paramPermutationKeys, _ := range paramPermutation {
realPromql := curPromql
split := strings.Split(paramPermutationKeys, JoinMark)
for j := range ParamKeys {
realPromql = fillVar(realPromql, ParamKeys[j], split[j])
}
keyToPromql[paramPermutationKeys] = realPromql
}
// 并发查询
wg := sync.WaitGroup{}
semaphore := make(chan struct{}, 200)
for key, promql := range keyToPromql {
wg.Add(1)
semaphore <- struct{}{}
go func(key, promql string) {
defer func() {
<-semaphore
wg.Done()
}()
arw.Processor.Stats.CounterQueryDataTotal.WithLabelValues(fmt.Sprintf("%d", arw.DatasourceId), fmt.Sprintf("%d", arw.Rule.Id)).Inc()
value, _, err := readerClient.Query(context.Background(), promql, time.Now())
if err != nil {
logger.Errorf("alert_eval_%d datasource_%d promql:%s, error:%v", arw.Rule.Id, arw.DatasourceId, promql, err)
return
}
logger.Infof("alert_eval_%d datasource_%d promql:%s, value:%+v", arw.Rule.Id, arw.DatasourceId, promql, value)
points := models.ConvertAnomalyPoints(value)
if len(points) == 0 {
anomalyPointsMap.Delete(key)
return
}
for i := 0; i < len(points); i++ {
points[i].Severity = query.Severity
points[i].Query = promql
points[i].ValuesUnit = map[string]unit.FormattedValue{
"v": unit.ValueFormatter(query.Unit, 2, points[i].Value),
}
// 每个异常点都需要生成 key,子筛选使用 key 覆盖上层筛选,解决 issue https://github.com/ccfos/nightingale/issues/2433 提的问题
var cur []string
for _, paramKey := range ParamKeys {
val := string(points[i].Labels[model.LabelName(varToLabel[paramKey])])
cur = append(cur, val)
}
anomalyPointsMap.Store(strings.Join(cur, JoinMark), points[i])
}
}(key, promql)
}
wg.Wait()
}
curNode = curNode.ChildVarConfigs
}
anomalyPoints := make([]models.AnomalyPoint, 0)
anomalyPointsMap.Range(func(key, value any) bool {
if point, ok := value.(models.AnomalyPoint); ok {
anomalyPoints = append(anomalyPoints, point)
}
return true
})
return anomalyPoints
}
// 判断 query 中是否有会导致标签丢失的聚合函数
func hasLabelLossAggregator(query models.PromQuery) bool {
noLabelAggregators := []string{
"sum", "min", "max", "avg",
"stddev", "stdvar",
"count", "quantile",
"group",
}
promql := strings.ToLower(query.PromQl)
for _, fn := range noLabelAggregators {
// 检查是否包含这些聚合函数,需要确保函数名后面跟着左括号
if strings.Contains(promql, fn+"(") {
return true
}
}
return false
}
// 判断 query 中是否有 != =~ !~
func notExactMatch(query models.PromQuery) bool {
promql := strings.ToLower(query.PromQl)
if strings.Contains(promql, "!=") || strings.Contains(promql, "=~") || strings.Contains(promql, "!~") {
return true
}
return false
}
// ExtractVarMapping 从 promql 中提取变量映射关系,为了在 query 之后可以将标签正确的放回 promql
// 输入: sum(rate(mem_used_percent{host="$my_host"})) by (instance) + avg(node_load1{region="$region"}) > $val
// 输出: map[string]string{"my_host":"host", "region":"region"}
func ExtractVarMapping(promql string) map[string]string {
varMapping := make(map[string]string)
// 遍历所有花括号对
for {
start := strings.Index(promql, "{")
if start == -1 {
break
}
end := strings.Index(promql, "}")
if end == -1 {
break
}
// 提取标签键值对
labels := promql[start+1 : end]
pairs := strings.Split(labels, ",")
for _, pair := range pairs {
// 分割键值对
var kv []string
if strings.Contains(pair, "!=") {
kv = strings.Split(pair, "!=")
} else if strings.Contains(pair, "=~") {
kv = strings.Split(pair, "=~")
} else if strings.Contains(pair, "!~") {
kv = strings.Split(pair, "!~")
} else {
kv = strings.Split(pair, "=")
}
if len(kv) != 2 {
continue
}
key := strings.TrimSpace(kv[0])
value := strings.Trim(strings.TrimSpace(kv[1]), "\"")
value = strings.Trim(value, "'")
// 检查值是否为变量(以$开头)
if strings.HasPrefix(value, "$") {
varName := value[1:] // 去掉$前缀
varMapping[varName] = key
}
}
// 继续处理剩余部分
promql = promql[end+1:]
}
return varMapping
}
func fillVar(curRealQuery string, paramKey string, val string) string {
curRealQuery = strings.Replace(curRealQuery, fmt.Sprintf("'$%s'", paramKey), fmt.Sprintf("'%s'", val), -1)
curRealQuery = strings.Replace(curRealQuery, fmt.Sprintf("\"$%s\"", paramKey), fmt.Sprintf("\"%s\"", val), -1)
return curRealQuery
}
func (arw *AlertRuleWorker) GetAnomalyPoint(rule *models.AlertRule, dsId int64) ([]models.AnomalyPoint, []models.AnomalyPoint, error) {
// 获取查询和规则判断条件
start := time.Now()
defer func() {
arw.Processor.Stats.GaugeRuleEvalDuration.WithLabelValues(fmt.Sprintf("%v", arw.Rule.Id), fmt.Sprintf("%v", arw.Processor.DatasourceId())).Set(float64(time.Since(start).Milliseconds()))
}()
points := []models.AnomalyPoint{}
recoverPoints := []models.AnomalyPoint{}
ruleConfig := strings.TrimSpace(rule.RuleConfig)
if ruleConfig == "" {
logger.Warningf("alert_eval_%d datasource_%d ruleConfig is blank", rule.Id, dsId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_RULE_CONFIG, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
"",
).Set(0)
return points, recoverPoints, fmt.Errorf("alert_eval_%d datasource_%d ruleConfig is blank", rule.Id, dsId)
}
var ruleQuery models.RuleQuery
err := json.Unmarshal([]byte(ruleConfig), &ruleQuery)
if err != nil {
logger.Warningf("alert_eval_%d datasource_%d promql parse error:%s", rule.Id, dsId, err.Error())
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_RULE_CONFIG, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
return points, recoverPoints, fmt.Errorf("alert_eval_%d datasource_%d promql parse error:%s", rule.Id, dsId, err.Error())
}
arw.Inhibit = ruleQuery.Inhibit
if len(ruleQuery.Queries) > 0 {
seriesStore := make(map[uint64]models.DataResp)
seriesTagIndexes := make(map[string]map[uint64][]uint64, 0)
for i, query := range ruleQuery.Queries {
seriesTagIndex := make(map[uint64][]uint64)
plug, exists := dscache.DsCache.Get(rule.Cate, dsId)
if !exists {
logger.Warningf("alert_eval_%d datasource_%d not exists", rule.Id, dsId)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_CLIENT, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(-2)
return points, recoverPoints, fmt.Errorf("alert_eval_%d datasource_%d not exists", rule.Id, dsId)
}
if err = ExecuteQueryTemplate(rule.Cate, query, nil); err != nil {
logger.Warningf("alert_eval_%d datasource_%d execute query template error: %v", rule.Id, dsId, err)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), EXEC_TEMPLATE, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(-3)
}
ctx := context.WithValue(context.Background(), "delay", int64(rule.Delay))
series, err := plug.QueryData(ctx, query)
arw.Processor.Stats.CounterQueryDataTotal.WithLabelValues(fmt.Sprintf("%d", arw.DatasourceId), fmt.Sprintf("%d", rule.Id)).Inc()
if err != nil {
logger.Warningf("alert_eval_%d datasource_%d query data error: %v", rule.Id, dsId, err)
arw.Processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.Processor.DatasourceId()), GET_CLIENT, arw.Processor.BusiGroupCache.GetNameByBusiGroupId(arw.Rule.GroupId), fmt.Sprintf("%v", arw.Rule.Id)).Inc()
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(-1)
return points, recoverPoints, fmt.Errorf("alert_eval_%d datasource_%d query data error: %v", rule.Id, dsId, err)
}
arw.Processor.Stats.GaugeQuerySeriesCount.WithLabelValues(
fmt.Sprintf("%v", arw.Rule.Id),
fmt.Sprintf("%v", arw.Processor.DatasourceId()),
fmt.Sprintf("%v", i),
).Set(float64(len(series)))
// 此条日志很重要,是告警判断的现场值
logger.Infof("alert_eval_%d datasource_%d req:%+v resp:%v", rule.Id, dsId, query, series)
for i := 0; i < len(series); i++ {
seriesHash := hash.GetHash(series[i].Metric, series[i].Ref)
tagHash := hash.GetTagHash(series[i].Metric)
seriesStore[seriesHash] = series[i]
// 将曲线按照相同的 tag 分组
if _, exists := seriesTagIndex[tagHash]; !exists {
seriesTagIndex[tagHash] = make([]uint64, 0)
}
seriesTagIndex[tagHash] = append(seriesTagIndex[tagHash], seriesHash)
}
ref, err := GetQueryRef(query)
if err != nil {
logger.Warningf("alert_eval_%d datasource_%d query:%+v get ref error:%s", rule.Id, dsId, query, err.Error())
continue
}
seriesTagIndexes[ref] = seriesTagIndex
}
unitMap := make(map[string]string)
for _, query := range ruleQuery.Queries {
ref, unit, err := GetQueryRefAndUnit(query)
if err != nil {
logger.Warningf("alert_eval_%d datasource_%d query:%+v get ref and unit error:%s", rule.Id, dsId, query, err.Error())
continue
}
unitMap[ref] = unit
}
if !ruleQuery.ExpTriggerDisable {
for _, trigger := range ruleQuery.Triggers {
seriesTagIndex := ProcessJoins(rule.Id, trigger, seriesTagIndexes, seriesStore)
for _, seriesHash := range seriesTagIndex {
valuesUnitMap := make(map[string]unit.FormattedValue)
sort.Slice(seriesHash, func(i, j int) bool {
return seriesHash[i] < seriesHash[j]
})
m := make(map[string]interface{})
var ts int64
var sample models.DataResp
var value float64
for _, seriesHash := range seriesHash {
series, exists := seriesStore[seriesHash]
if !exists {
logger.Warningf("alert_eval_%d datasource_%d series:%+v not found", rule.Id, dsId, series)
continue
}
t, v, exists := series.Last()
if !exists {
logger.Warningf("alert_eval_%d datasource_%d series:%+v value not found", rule.Id, dsId, series)
continue
}
if !strings.Contains(trigger.Exp, "$"+series.Ref) {
// 表达式中不包含该变量
continue
}
m["$"+series.Ref] = v
m["$"+series.Ref+"."+series.MetricName()] = v
for k, v := range series.Metric {
if k == "__name__" {
continue
}
if !strings.Contains(trigger.Exp, "$"+series.Ref+"."+string(k)) {
// 过滤掉表达式中不包含的标签
continue
}
m["$"+series.Ref+"."+string(k)] = string(v)
}
if u, exists := unitMap[series.Ref]; exists {
valuesUnitMap["$"+series.Ref+"."+series.MetricName()] = unit.ValueFormatter(u, 2, v)
}
ts = int64(t)
sample = series
value = v
logger.Infof("alert_eval_%d datasource_%d origin series labels:%+v", rule.Id, dsId, series.Metric)
}
isTriggered := parser.CalcWithRid(trigger.Exp, m, rule.Id)
// 此条日志很重要,是告警判断的现场值
logger.Infof("alert_eval_%d datasource_%d trigger:%+v exp:%s res:%v m:%v", rule.Id, dsId, trigger, trigger.Exp, isTriggered, m)
var values string
for k, v := range m {
if !strings.Contains(k, ".") {
continue
}
if u, exists := valuesUnitMap[k]; exists { // 配置了单位,优先用配置了单位的值
values += fmt.Sprintf("%s:%s ", k, u.Text)
} else {
switch v.(type) {
case float64:
values += fmt.Sprintf("%s:%.3f ", k, v)
case string:
values += fmt.Sprintf("%s:%s ", k, v)
}
}
}
queries := ruleQuery.Queries
if sample.Query != "" {
queries = []interface{}{sample.Query}
}
point := models.AnomalyPoint{
Key: sample.MetricName(),
Labels: sample.Metric,
Timestamp: int64(ts),
Value: value,
Values: values,
Severity: trigger.Severity,
Triggered: isTriggered,
Query: fmt.Sprintf("query:%+v trigger:%+v", queries, trigger),
RecoverConfig: trigger.RecoverConfig,
ValuesUnit: valuesUnitMap,
}
if isTriggered {
points = append(points, point)
} else {
switch trigger.RecoverConfig.JudgeType {
case models.Origin:
// do nothing
case models.RecoverOnCondition:
fulfill := parser.CalcWithRid(trigger.RecoverConfig.RecoverExp, m, rule.Id)
if !fulfill {
continue
}
}
recoverPoints = append(recoverPoints, point)
}
}
}
}
if ruleQuery.NodataTrigger.Enable {
now := time.Now().Unix()
// 使用 arw.LastSeriesStore 检查上次查询结果
if len(arw.LastSeriesStore) > 0 {
// 遍历上次的曲线数据
for hash, lastSeries := range arw.LastSeriesStore {
if ruleQuery.NodataTrigger.ResolveAfterEnable {
lastTs, _, exists := lastSeries.Last()
if !exists {
continue
}
// 检查是否超过 resolve_after 时间
if now-int64(lastTs) > int64(ruleQuery.NodataTrigger.ResolveAfter) {
logger.Infof("alert_eval_%d datasource_%d series:%+v resolve after %d seconds now:%d lastTs:%d", rule.Id, dsId, lastSeries, ruleQuery.NodataTrigger.ResolveAfter, now, int64(lastTs))
delete(arw.LastSeriesStore, hash)
continue
}
}
// 检查是否在本次查询结果中存在
if _, exists := seriesStore[hash]; !exists {
// 生成无数据告警点
point := models.AnomalyPoint{
Key: lastSeries.MetricName(),
Labels: lastSeries.Metric,
Timestamp: now,
Value: 0,
Values: fmt.Sprintf("nodata since %v", time.Unix(now, 0).Format("2006-01-02 15:04:05")),
Severity: ruleQuery.NodataTrigger.Severity,
Triggered: true,
Query: fmt.Sprintf("nodata check for %s", lastSeries.LabelsString()),
TriggerType: models.TriggerTypeNodata,
}
points = append(points, point)
logger.Infof("alert_eval_%d datasource_%d nodata point:%+v", rule.Id, dsId, point)
}
}
}
// 更新 arw.LastSeriesStore
for hash, series := range seriesStore {
arw.LastSeriesStore[hash] = series
}
}
}
return points, recoverPoints, nil
}
// ExecuteQueryTemplate 根据数据源类型对 Query 进行模板渲染处理
// cate: 数据源类别,如 "mysql", "pgsql" 等
// query: 查询对象,如果是数据库类型的数据源,会处理其中的 sql 字段
// data: 模板数据对象,如果为 nil 则使用空结构体(不支持变量渲染),如果不为 nil 则使用传入的数据(支持变量渲染)
func ExecuteQueryTemplate(cate string, query interface{}, data interface{}) error {
// 检查 query 是否是 map,且包含 sql 字段
queryMap, ok := query.(map[string]interface{})
if !ok {
return nil
}
sqlVal, exists := queryMap["sql"]
if !exists {
return nil
}
sqlStr, ok := sqlVal.(string)
if !ok {
return nil
}
// 调用 ExecuteSqlTemplate 处理 sql 字段
processedSQL, err := ExecuteSqlTemplate(sqlStr, data)
if err != nil {
return fmt.Errorf("execute sql template error: %w", err)
}
// 更新 query 中的 sql 字段
queryMap["sql"] = processedSQL
return nil
}
// ExecuteSqlTemplate 执行 query 中的 golang 模板语法函数
// query: 要处理的 query 字符串
// data: 模板数据对象,如果为 nil 则使用空结构体(不支持变量渲染),如果不为 nil 则使用传入的数据(支持变量渲染)
func ExecuteSqlTemplate(query string, data interface{}) (string, error) {
if !strings.Contains(query, "{{") || !strings.Contains(query, "}}") {
return query, nil
}
tmpl, err := template.New("query").Funcs(tplx.TemplateFuncMap).Parse(query)
if err != nil {
return "", fmt.Errorf("query tmpl parse error: %w", err)
}
var buf strings.Builder
templateData := data
if templateData == nil {
templateData = struct{}{}
}
if err := tmpl.Execute(&buf, templateData); err != nil {
return "", fmt.Errorf("query tmpl execute error: %w", err)
}
return buf.String(), nil
}
================================================
FILE: alert/eval/eval_test.go
================================================
package eval
import (
"reflect"
"testing"
"golang.org/x/exp/slices"
)
var (
reHashTagIndex1 = map[uint64][][]uint64{
1: {
{1, 2}, {3, 4},
},
2: {
{5, 6}, {7, 8},
},
}
reHashTagIndex2 = map[uint64][][]uint64{
1: {
{9, 10}, {11, 12},
},
3: {
{13, 14}, {15, 16},
},
}
seriesTagIndex1 = map[uint64][]uint64{
1: {1, 2, 3, 4},
2: {5, 6, 7, 8},
}
seriesTagIndex2 = map[uint64][]uint64{
1: {9, 10, 11, 12},
3: {13, 14, 15, 16},
}
)
func Test_originalJoin(t *testing.T) {
type args struct {
seriesTagIndex1 map[uint64][]uint64
seriesTagIndex2 map[uint64][]uint64
}
tests := []struct {
name string
args args
want map[uint64][]uint64
}{
{
name: "original join",
args: args{
seriesTagIndex1: map[uint64][]uint64{
1: {1, 2, 3, 4},
2: {5, 6, 7, 8},
},
seriesTagIndex2: map[uint64][]uint64{
1: {9, 10, 11, 12},
3: {13, 14, 15, 16},
},
},
want: map[uint64][]uint64{
1: {1, 2, 3, 4, 9, 10, 11, 12},
2: {5, 6, 7, 8},
3: {13, 14, 15, 16},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := originalJoin(tt.args.seriesTagIndex1, tt.args.seriesTagIndex2); !reflect.DeepEqual(got, tt.want) {
t.Errorf("originalJoin() = %v, want %v", got, tt.want)
}
})
}
}
func Test_exclude(t *testing.T) {
type args struct {
reHashTagIndex1 map[uint64][][]uint64
reHashTagIndex2 map[uint64][][]uint64
}
tests := []struct {
name string
args args
want map[uint64][]uint64
}{
{
name: "left exclude",
args: args{
reHashTagIndex1: reHashTagIndex1,
reHashTagIndex2: reHashTagIndex2,
},
want: map[uint64][]uint64{
0: {5, 6},
1: {7, 8},
},
},
{
name: "right exclude",
args: args{
reHashTagIndex1: reHashTagIndex2,
reHashTagIndex2: reHashTagIndex1,
},
want: map[uint64][]uint64{
3: {13, 14},
4: {15, 16},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := exclude(tt.args.reHashTagIndex1, tt.args.reHashTagIndex2); !allValueDeepEqual(flatten(got), tt.want) {
t.Errorf("exclude() = %v, want %v", got, tt.want)
}
})
}
}
func Test_noneJoin(t *testing.T) {
type args struct {
seriesTagIndex1 map[uint64][]uint64
seriesTagIndex2 map[uint64][]uint64
}
tests := []struct {
name string
args args
want map[uint64][]uint64
}{
{
name: "none join, direct splicing",
args: args{
seriesTagIndex1: seriesTagIndex1,
seriesTagIndex2: seriesTagIndex2,
},
want: map[uint64][]uint64{
0: {1, 2, 3, 4},
1: {5, 6, 7, 8},
2: {9, 10, 11, 12},
3: {13, 14, 15, 16},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := noneJoin(tt.args.seriesTagIndex1, tt.args.seriesTagIndex2); !allValueDeepEqual(got, tt.want) {
t.Errorf("noneJoin() = %v, want %v", got, tt.want)
}
})
}
}
func Test_cartesianJoin(t *testing.T) {
type args struct {
seriesTagIndex1 map[uint64][]uint64
seriesTagIndex2 map[uint64][]uint64
}
tests := []struct {
name string
args args
want map[uint64][]uint64
}{
{
name: "cartesian join",
args: args{
seriesTagIndex1: seriesTagIndex1,
seriesTagIndex2: seriesTagIndex2,
},
want: map[uint64][]uint64{
0: {1, 2, 3, 4, 9, 10, 11, 12},
1: {5, 6, 7, 8, 9, 10, 11, 12},
2: {5, 6, 7, 8, 13, 14, 15, 16},
3: {1, 2, 3, 4, 13, 14, 15, 16},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := cartesianJoin(tt.args.seriesTagIndex1, tt.args.seriesTagIndex2); !allValueDeepEqual(got, tt.want) {
t.Errorf("cartesianJoin() = %v, want %v", got, tt.want)
}
})
}
}
func Test_onJoin(t *testing.T) {
type args struct {
reHashTagIndex1 map[uint64][][]uint64
reHashTagIndex2 map[uint64][][]uint64
joinType JoinType
}
tests := []struct {
name string
args args
want map[uint64][]uint64
}{
{
name: "left join",
args: args{
reHashTagIndex1: reHashTagIndex1,
reHashTagIndex2: reHashTagIndex2,
joinType: Left,
},
want: map[uint64][]uint64{
1: {1, 2, 9, 10},
2: {3, 4, 9, 10},
3: {1, 2, 11, 12},
4: {3, 4, 11, 12},
5: {5, 6},
6: {7, 8},
},
},
{
name: "right join",
args: args{
reHashTagIndex1: reHashTagIndex2,
reHashTagIndex2: reHashTagIndex1,
joinType: Right,
},
want: map[uint64][]uint64{
1: {1, 2, 9, 10},
2: {3, 4, 9, 10},
3: {1, 2, 11, 12},
4: {3, 4, 11, 12},
5: {13, 14},
6: {15, 16},
},
},
{
name: "inner join",
args: args{
reHashTagIndex1: reHashTagIndex1,
reHashTagIndex2: reHashTagIndex2,
joinType: Inner,
},
want: map[uint64][]uint64{
1: {1, 2, 9, 10},
2: {3, 4, 9, 10},
3: {1, 2, 11, 12},
4: {3, 4, 11, 12},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := onJoin(tt.args.reHashTagIndex1, tt.args.reHashTagIndex2, tt.args.joinType); !allValueDeepEqual(flatten(got), tt.want) {
t.Errorf("onJoin() = %v, want %v", got, tt.want)
}
})
}
}
// allValueDeepEqual 判断 map 的 value 是否相同,不考虑 key
func allValueDeepEqual(got, want map[uint64][]uint64) bool {
if len(got) != len(want) {
return false
}
for _, v1 := range got {
curEqual := false
slices.Sort(v1)
for _, v2 := range want {
slices.Sort(v2)
if reflect.DeepEqual(v1, v2) {
curEqual = true
break
}
}
if !curEqual {
return false
}
}
return true
}
// allValueDeepEqualOmitOrder 判断两个字符串切片是否相等,不考虑顺序
func allValueDeepEqualOmitOrder(got, want []string) bool {
if len(got) != len(want) {
return false
}
slices.Sort(got)
slices.Sort(want)
for i := range got {
if got[i] != want[i] {
return false
}
}
return true
}
func Test_removeVal(t *testing.T) {
type args struct {
promql string
}
tests := []struct {
name string
args args
want string
}{
// TODO: Add test cases.
{
name: "removeVal1",
args: args{
promql: "mem{test1=\"$test1\",test2=\"$test2\",test3=\"$test3\"} > $val",
},
want: "mem{} > $val",
},
{
name: "removeVal2",
args: args{
promql: "mem{test1=\"test1\",test2=\"$test2\",test3=\"$test3\"} > $val",
},
want: "mem{test1=\"test1\"} > $val",
},
{
name: "removeVal3",
args: args{
promql: "mem{test1=\"$test1\",test2=\"test2\",test3=\"$test3\"} > $val",
},
want: "mem{test2=\"test2\"} > $val",
},
{
name: "removeVal4",
args: args{
promql: "mem{test1=\"$test1\",test2=\"$test2\",test3=\"test3\"} > $val",
},
want: "mem{test3=\"test3\"} > $val",
},
{
name: "removeVal5",
args: args{
promql: "mem{test1=\"$test1\",test2=\"test2\",test3=\"test3\"} > $val",
},
want: "mem{test2=\"test2\",test3=\"test3\"} > $val",
},
{
name: "removeVal6",
args: args{
promql: "mem{test1=\"test1\",test2=\"$test2\",test3=\"test3\"} > $val",
},
want: "mem{test1=\"test1\",test3=\"test3\"} > $val",
},
{
name: "removeVal7",
args: args{
promql: "mem{test1=\"test1\",test2=\"test2\",test3='$test3'} > $val",
},
want: "mem{test1=\"test1\",test2=\"test2\"} > $val",
},
{
name: "removeVal8",
args: args{
promql: "mem{test1=\"test1\",test2=\"test2\",test3=\"test3\"} > $val",
},
want: "mem{test1=\"test1\",test2=\"test2\",test3=\"test3\"} > $val",
},
{
name: "removeVal9",
args: args{
promql: "mem{test1=\"$test1\",test2=\"test2\"} > $val1 and mem{test3=\"test3\",test4=\"test4\"} > $val2",
},
want: "mem{test2=\"test2\"} > $val1 and mem{test3=\"test3\",test4=\"test4\"} > $val2",
},
{
name: "removeVal10",
args: args{
promql: "mem{test1=\"test1\",test2='$test2'} > $val1 and mem{test3=\"test3\",test4=\"test4\"} > $val2",
},
want: "mem{test1=\"test1\"} > $val1 and mem{test3=\"test3\",test4=\"test4\"} > $val2",
},
{
name: "removeVal11",
args: args{
promql: "mem{test1='test1',test2=\"test2\"} > $val1 and mem{test3=\"$test3\",test4=\"test4\"} > $val2",
},
want: "mem{test1='test1',test2=\"test2\"} > $val1 and mem{test4=\"test4\"} > $val2",
},
{
name: "removeVal12",
args: args{
promql: "mem{test1=\"test1\",test2=\"test2\"} > $val1 and mem{test3=\"test3\",test4=\"$test4\"} > $val2",
},
want: "mem{test1=\"test1\",test2=\"test2\"} > $val1 and mem{test3=\"test3\"} > $val2",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := removeVal(tt.args.promql); got != tt.want {
t.Errorf("removeVal() = %v, want %v", got, tt.want)
}
})
}
}
func TestExtractVarMapping(t *testing.T) {
tests := []struct {
name string
promql string
want map[string]string
}{
{
name: "单个花括号单个变量",
promql: `mem_used_percent{host="$my_host"} > $val`,
want: map[string]string{"my_host": "host"},
},
{
name: "单个花括号多个变量",
promql: `mem_used_percent{host="$my_host",region="$region",env="prod"} > $val`,
want: map[string]string{"my_host": "host", "region": "region"},
},
{
name: "多个花括号多个变量",
promql: `sum(rate(mem_used_percent{host="$my_host"})) by (instance) + avg(node_load1{region="$region"}) > $val`,
want: map[string]string{"my_host": "host", "region": "region"},
},
{
name: "相同变量出现多次",
promql: `sum(rate(mem_used_percent{host="$my_host"})) + avg(node_load1{host="$my_host"}) > $val`,
want: map[string]string{"my_host": "host"},
},
{
name: "没有变量",
promql: `mem_used_percent{host="localhost",region="cn"} > 80`,
want: map[string]string{},
},
{
name: "没有花括号",
promql: `80 > $val`,
want: map[string]string{},
},
{
name: "格式不规范的标签",
promql: `mem_used_percent{host=$my_host,region = $region} > $val`,
want: map[string]string{"my_host": "host", "region": "region"},
},
{
name: "空花括号",
promql: `mem_used_percent{} > $val`,
want: map[string]string{},
},
{
name: "不完整的花括号",
promql: `mem_used_percent{host="$my_host"`,
want: map[string]string{},
},
{
name: "复杂表达式",
promql: `sum(rate(http_requests_total{handler="$handler",code="$code"}[5m])) by (handler) / sum(rate(http_requests_total{handler="$handler"}[5m])) by (handler) * 100 > $threshold`,
want: map[string]string{"handler": "handler", "code": "code"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ExtractVarMapping(tt.promql)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ExtractVarMapping() = %v, want %v", got, tt.want)
}
})
}
}
================================================
FILE: alert/mute/mute.go
================================================
package mute
import (
"slices"
"strconv"
"strings"
"time"
"github.com/ccfos/nightingale/v6/alert/common"
"github.com/ccfos/nightingale/v6/memsto"
"github.com/ccfos/nightingale/v6/models"
"github.com/pkg/errors"
"github.com/toolkits/pkg/logger"
)
func IsMuted(rule *models.AlertRule, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType, alertMuteCache *memsto.AlertMuteCacheType) (bool, string, int64) {
if rule.Disabled == 1 {
return true, "rule disabled", 0
}
if TimeSpanMuteStrategy(rule, event) {
return true, "rule is not effective for period of time", 0
}
if IdentNotExistsMuteStrategy(rule, event, targetCache) {
return true, "ident not exists mute", 0
}
if BgNotMatchMuteStrategy(rule, event, targetCache) {
return true, "bg not match mute", 0
}
hit, muteId := EventMuteStrategy(event, alertMuteCache)
if hit {
return true, "match mute rule", muteId
}
return false, "", 0
}
// TimeSpanMuteStrategy 根据规则配置的告警生效时间段过滤,如果产生的告警不在规则配置的告警生效时间段内,则不告警,即被mute
// 时间范围,左闭右开,默认范围:00:00-24:00
// 如果规则配置了时区,则在该时区下进行时间判断;如果时区为空,则使用系统时区
func TimeSpanMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent) bool {
// 确定使用的时区
var targetLoc *time.Location
var err error
timezone := rule.TimeZone
if timezone == "" {
// 如果时区为空,使用系统时区(保持原有逻辑)
targetLoc = time.Local
} else {
// 加载规则配置的时区
targetLoc, err = time.LoadLocation(timezone)
if err != nil {
// 如果时区加载失败,记录错误并使用系统时区
logger.Warningf("Failed to load timezone %s for rule %d, using system timezone: %v", timezone, rule.Id, err)
targetLoc = time.Local
}
}
// 将触发时间转换到目标时区
tm := time.Unix(event.TriggerTime, 0).In(targetLoc)
triggerTime := tm.Format("15:04")
triggerWeek := strconv.Itoa(int(tm.Weekday()))
if rule.EnableDaysOfWeek == "" {
// 如果规则没有配置生效时间,则默认全天生效
return false
}
enableStime := strings.Fields(rule.EnableStime)
enableEtime := strings.Fields(rule.EnableEtime)
enableDaysOfWeek := strings.Split(rule.EnableDaysOfWeek, ";")
length := len(enableDaysOfWeek)
// enableStime,enableEtime,enableDaysOfWeek三者长度肯定相同,这里循环一个即可
for i := 0; i < length; i++ {
enableDaysOfWeek[i] = strings.Replace(enableDaysOfWeek[i], "7", "0", 1)
if !strings.Contains(enableDaysOfWeek[i], triggerWeek) {
continue
}
if enableStime[i] < enableEtime[i] {
if enableEtime[i] == "23:59" {
// 02:00-23:59,这种情况做个特殊处理,相当于左闭右闭区间了
if triggerTime < enableStime[i] {
// mute, 即没生效
continue
}
} else {
// 02:00-04:00 或者 02:00-24:00
if triggerTime < enableStime[i] || triggerTime >= enableEtime[i] {
// mute, 即没生效
continue
}
}
} else if enableStime[i] > enableEtime[i] {
// 21:00-09:00
if triggerTime < enableStime[i] && triggerTime >= enableEtime[i] {
// mute, 即没生效
continue
}
}
// 到这里说明当前时刻在告警规则的某组生效时间范围内,即没有 mute,直接返回 false
return false
}
return true
}
// IdentNotExistsMuteStrategy 根据ident是否存在过滤,如果ident不存在,则target_up的告警直接过滤掉
func IdentNotExistsMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType) bool {
ident, has := event.TagsMap["ident"]
if !has {
return false
}
_, exists := targetCache.Get(ident)
// 如果是target_up的告警,且ident已经不存在了,直接过滤掉
// 这里的判断有点太粗暴了,但是目前没有更好的办法
if !exists && strings.Contains(rule.PromQl, "target_up") {
logger.Debugf("alert_eval_%d [IdentNotExistsMuteStrategy] mute: cluster:%s ident:%s", rule.Id, event.Cluster, ident)
return true
}
return false
}
// BgNotMatchMuteStrategy 当规则开启只在bg内部告警时,对于非bg内部的机器过滤
func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent, targetCache *memsto.TargetCacheType) bool {
// 没有开启BG内部告警,直接不过滤
if rule.EnableInBG == 0 {
return false
}
ident, has := event.TagsMap["ident"]
if !has {
return false
}
target, exists := targetCache.Get(ident)
// 对于包含ident的告警事件,check一下ident所属bg和rule所属bg是否相同
// 如果告警规则选择了只在本BG生效,那其他BG的机器就不能因此规则产生告警
if exists && !target.MatchGroupId(rule.GroupId) {
logger.Debugf("alert_eval_%d [BgNotMatchMuteStrategy] mute: cluster:%s", rule.Id, event.Cluster)
return true
}
return false
}
func EventMuteStrategy(event *models.AlertCurEvent, alertMuteCache *memsto.AlertMuteCacheType) (bool, int64) {
mutes, has := alertMuteCache.Gets(event.GroupId)
if !has || len(mutes) == 0 {
return false, 0
}
for i := 0; i < len(mutes); i++ {
matched, _ := MatchMute(event, mutes[i])
if matched {
return true, mutes[i].Id
}
}
return false, 0
}
// MatchMute 如果传入了clock这个可选参数,就表示使用这个clock表示的时间,否则就从event的字段中取TriggerTime
func MatchMute(event *models.AlertCurEvent, mute *models.AlertMute, clock ...int64) (bool, error) {
if mute.Disabled == 1 {
return false, errors.New("mute is disabled")
}
// 如果不是全局的,判断 匹配的 datasource id
if len(mute.DatasourceIdsJson) != 0 && mute.DatasourceIdsJson[0] != 0 && event.DatasourceId != 0 {
if !slices.Contains(mute.DatasourceIdsJson, event.DatasourceId) {
return false, errors.New("datasource id not match")
}
}
if mute.MuteTimeType == models.TimeRange {
if !mute.IsWithinTimeRange(event.TriggerTime) {
return false, errors.New("event trigger time not within mute time range")
}
} else if mute.MuteTimeType == models.Periodic {
ts := event.TriggerTime
if len(clock) > 0 {
ts = clock[0]
}
if !mute.IsWithinPeriodicMute(ts) {
return false, errors.New("event trigger time not within periodic mute range")
}
} else {
logger.Warningf("mute time type invalid, %d", mute.MuteTimeType)
return false, errors.New("mute time type invalid")
}
var matchSeverity bool
if len(mute.SeveritiesJson) > 0 {
for _, s := range mute.SeveritiesJson {
if event.Severity == s || s == 0 {
matchSeverity = true
break
}
}
} else {
matchSeverity = true
}
if !matchSeverity {
return false, errors.New("event severity not match mute severity")
}
if len(mute.ITags) == 0 {
return true, nil
}
if !common.MatchTags(event.TagsMap, mute.ITags) {
return false, errors.New("event tags not match mute tags")
}
return true, nil
}
================================================
FILE: alert/naming/hashring.go
================================================
package naming
import (
"errors"
"sync"
"github.com/toolkits/pkg/consistent"
"github.com/toolkits/pkg/logger"
)
const NodeReplicas = 500
type DatasourceHashRingType struct {
sync.RWMutex
Rings map[string]*consistent.Consistent
}
// for alert_rule sharding
var HostDatasource int64 = 99999999
var DatasourceHashRing = DatasourceHashRingType{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(datasourceId string, 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 %s rebuild %+v", datasourceId, r.Members())
}
func (chr *DatasourceHashRingType) GetNode(datasourceId string, pk string) (string, error) {
chr.Lock()
defer chr.Unlock()
_, exists := chr.Rings[datasourceId]
if !exists {
chr.Rings[datasourceId] = NewConsistentHashRing(int32(NodeReplicas), []string{})
}
return chr.Rings[datasourceId].Get(pk)
}
func (chr *DatasourceHashRingType) IsHit(datasourceId string, pk string, currentNode string) bool {
node, err := chr.GetNode(datasourceId, pk)
if err != nil {
if !errors.Is(err, consistent.ErrEmptyCircle) {
logger.Errorf("rule id:%s is not work, datasource id:%s failed to get node from hashring:%v", pk, datasourceId, err)
}
return false
}
return node == currentNode
}
func (chr *DatasourceHashRingType) Set(datasourceId string, r *consistent.Consistent) {
chr.Lock()
defer chr.Unlock()
chr.Rings[datasourceId] = r
}
func (chr *DatasourceHashRingType) Del(datasourceId string) {
chr.Lock()
defer chr.Unlock()
delete(chr.Rings, datasourceId)
}
func (chr *DatasourceHashRingType) Clear(engineName string) {
chr.Lock()
defer chr.Unlock()
for id := range chr.Rings {
if id == engineName {
continue
}
delete(chr.Rings, id)
}
}
================================================
FILE: alert/naming/heartbeat.go
================================================
package naming
import (
"fmt"
"sort"
"strings"
"time"
"github.com/ccfos/nightingale/v6/alert/aconf"
"github.com/ccfos/nightingale/v6/alert/astats"
"github.com/ccfos/nightingale/v6/models"
"github.com/ccfos/nightingale/v6/pkg/ctx"
"github.com/ccfos/nightingale/v6/pkg/poster"
"github.com/toolkits/pkg/logger"
)
type Naming struct {
ctx *ctx.Context
heartbeatConfig aconf.HeartbeatConfig
astats *astats.Stats
}
func NewNaming(ctx *ctx.Context, heartbeat aconf.HeartbeatConfig, alertStats *astats.Stats) *Naming {
naming := &Naming{
ctx: ctx,
heartbeatConfig: heartbeat,
astats: alertStats,
}
naming.Heartbeats()
return naming
}
// local servers
var localss map[int64]string
var localHostServers map[string]string
func (n *Naming) Heartbeats() error {
localss = make(map[int64]string)
localHostServers = make(map[string]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() {
if !n.ctx.IsCenter {
return
}
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.heartbeatConfig.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.GetDatasourceIdsByEngineName(n.ctx, n.heartbeatConfig.EngineName)
if err != nil {
return err
}
if len(datasourceIds) == 0 {
err := models.AlertingEngineHeartbeatWithCluster(n.ctx, n.heartbeatConfig.Endpoint, n.heartbeatConfig.EngineName, 0)
if err != nil {
logger.Warningf("heartbeat with cluster %s err:%v", "", err)
n.astats.CounterHeartbeatErrorTotal.WithLabelValues().Inc()
}
} else {
for i := 0; i < len(datasourceIds); i++ {
err := models.AlertingEngineHeartbeatWithCluster(n.ctx, n.heartbeatConfig.Endpoint, n.heartbeatConfig.EngineName, datasourceIds[i])
if err != nil {
logger.Warningf("heartbeat with cluster %d err:%v", datasourceIds[i], err)
n.astats.CounterHeartbeatErrorTotal.WithLabelValues().Inc()
}
}
}
if len(datasourceIds) == 0 {
DatasourceHashRing.Clear(n.heartbeatConfig.EngineName)
for dsId := range localss {
delete(localss, dsId)
}
}
newDatasource := make(map[int64]struct{})
for i := 0; i < len(datasourceIds); i++ {
newDatasource[datasourceIds[i]] = struct{}{}
servers, err := n.ActiveServers(datasourceIds[i])
if err != nil {
logger.Warningf("heartbeat %d get active server err:%v", datasourceIds[i], err)
n.astats.CounterHeartbeatErrorTotal.WithLabelValues().Inc()
continue
}
sort.Strings(servers)
newss := strings.Join(servers, " ")
oldss, exists := localss[datasourceIds[i]]
if exists && oldss == newss {
continue
}
RebuildConsistentHashRing(fmt.Sprintf("%d", datasourceIds[i]), servers)
localss[datasourceIds[i]] = newss
}
for dsId := range localss {
if _, exists := newDatasource[dsId]; !exists {
delete(localss, dsId)
DatasourceHashRing.Del(fmt.Sprintf("%d", dsId))
}
}
// host 告警使用的是 hash ring
err = models.AlertingEngineHeartbeatWithCluster(n.ctx, n.heartbeatConfig.Endpoint, n.heartbeatConfig.EngineName, HostDatasource)
if err != nil {
logger.Warningf("heartbeat with cluster %s err:%v", "", err)
n.astats.CounterHeartbeatErrorTotal.WithLabelValues().Inc()
}
servers, err := n.ActiveServersByEngineName()
if err != nil {
logger.Warningf("heartbeat %d get active server err:%v", HostDatasource, err)
n.astats.CounterHeartbeatErrorTotal.WithLabelValues().Inc()
return nil
}
sort.Strings(servers)
newss := strings.Join(servers, " ")
oldss, exists := localHostServers[n.heartbeatConfig.EngineName]
if exists && oldss == newss {
return nil
}
RebuildConsistentHashRing(n.heartbeatConfig.EngineName, servers)
localHostServers[n.heartbeatConfig.EngineName] = newss
return nil
}
func (n *Naming) ActiveServers(datasourceId int64) ([]string, error) {
if datasourceId == -1 {
return nil, fmt.Errorf("cluster is empty")
}
if !n.ctx.IsCenter {
lst, err := poster.GetByUrls[[]string](n.ctx, "/v1/n9e/servers-active?dsid="+fmt.Sprintf("%d", datasourceId))
return lst, err
}
// 30秒内有心跳,就认为是活的
return models.AlertingEngineGetsInstances(n.ctx, "datasource_id = ? and clock > ?", datasourceId, time.Now().Unix()-30)
}
func (n *Naming) ActiveServersByEngineName() ([]string, error) {
if !n.ctx.IsCenter {
lst, err := poster.GetByUrls[[]string](n.ctx, "/v1/n9e/servers-active?engine_name="+n.heartbeatConfig.EngineName)
return lst, err
}
// 30秒内有心跳,就认为是活的
return models.AlertingEngineGetsInstances(n.ctx, "engine_cluster = ? and clock > ?", n.heartbeatConfig.EngineName, time.Now().Unix()-30)
}
================================================
FILE: alert/naming/leader.go
================================================
package naming
import (
"sort"
"github.com/toolkits/pkg/logger"
)
func (n *Naming) IamLeader() bool {
if !n.ctx.IsCenter {
return false
}
servers, err := n.ActiveServersByEngineName()
if err != nil {
logger.Errorf("failed to get active servers: %v", err)
return false
}
if len(servers) == 0 {
logger.Errorf("active servers empty")
return false
}
sort.Strings(servers)
return n.heartbeatConfig.Endpoint == servers[0]
}
================================================
FILE: alert/pipeline/engine/engine.go
================================================
package engine
import (
"fmt"
"time"
"github.com/ccfos/nightingale/v6/models"
"github.com/ccfos/nightingale/v6/pkg/ctx"
"github.com/google/uuid"
"github.com/toolkits/pkg/logger"
)
type WorkflowEngine struct {
ctx *ctx.Context
}
func NewWorkflowEngine(c *ctx.Context) *WorkflowEngine {
return &WorkflowEngine{ctx: c}
}
func (e *WorkflowEngine) Execute(pipeline *models.EventPipeline, event *models.AlertCurEvent, triggerCtx *models.WorkflowTriggerContext) (*models.AlertCurEvent, *models.WorkflowResult, error) {
startTime := time.Now()
wfCtx := e.initWorkflowContext(pipeline, event, triggerCtx)
nodes := pipeline.GetWorkflowNodes()
connections := pipeline.GetWorkflowConnections()
if len(nodes) == 0 {
return event, &models.WorkflowResult{
Event: event,
Status: models.ExecutionStatusSuccess,
Message: "no nodes to execute",
}, nil
}
nodeMap := make(map[string]*models.WorkflowNode)
for i := range nodes {
if nodes[i].RetryInterval == 0 {
nodes[i].RetryInterval = 1
}
if nodes[i].MaxRetries == 0 {
nodes[i].MaxRetries = 1
}
nodeMap[nodes[i].ID] = &nodes[i]
}
result := e.executeDAG(nodeMap, connections, wfCtx)
result.Event = wfCtx.Event
duration := time.Since(startTime).Milliseconds()
if triggerCtx != nil && triggerCtx.Mode != "" {
e.saveExecutionRecord(pipeline, wfCtx, result, triggerCtx, startTime.Unix(), duration)
}
return wfCtx.Event, result, nil
}
func (e *WorkflowEngine) initWorkflowContext(pipeline *models.EventPipeline, event *models.AlertCurEvent, triggerCtx *models.WorkflowTriggerContext) *models.WorkflowContext {
// 合并输入参数
inputs := pipeline.GetInputsMap()
if triggerCtx != nil && triggerCtx.InputsOverrides != nil {
for k, v := range triggerCtx.InputsOverrides {
inputs[k] = v
}
}
metadata := map[string]string{
"start_time": fmt.Sprintf("%d", time.Now().Unix()),
"pipeline_id": fmt.Sprintf("%d", pipeline.ID),
}
// 是否启用流式输出
stream := false
if triggerCtx != nil {
metadata["request_id"] = triggerCtx.RequestID
metadata["trigger_mode"] = triggerCtx.Mode
metadata["trigger_by"] = triggerCtx.TriggerBy
stream = triggerCtx.Stream
}
return &models.WorkflowContext{
Event: event,
Inputs: inputs,
Vars: make(map[string]interface{}), // 初始化空的 Vars,供节点间传递数据
Metadata: metadata,
Stream: stream,
}
}
// executeDAG 使用 Kahn 算法执行 DAG
func (e *WorkflowEngine) executeDAG(nodeMap map[string]*models.WorkflowNode, connections models.Connections, wfCtx *models.WorkflowContext) *models.WorkflowResult {
result := &models.WorkflowResult{
Status: models.ExecutionStatusSuccess,
NodeResults: make([]*models.NodeExecutionResult, 0),
Stream: wfCtx.Stream, // 从上下文继承流式输出设置
}
// 计算每个节点的入度
inDegree := make(map[string]int)
for nodeID := range nodeMap {
inDegree[nodeID] = 0
}
// 遍历连接,计算入度
for _, nodeConns := range connections {
for _, targets := range nodeConns.Main {
for _, target := range targets {
inDegree[target.Node]++
}
}
}
// 找到所有入度为 0 的节点(起始节点)
queue := make([]string, 0)
for nodeID, degree := range inDegree {
if degree == 0 {
queue = append(queue, nodeID)
}
}
// 如果没有起始节点,说明存在循环依赖
if len(queue) == 0 && len(nodeMap) > 0 {
result.Status = models.ExecutionStatusFailed
result.Message = "workflow has circular dependency"
return result
}
// 记录已执行的节点
executed := make(map[string]bool)
// 记录节点的分支选择结果
branchResults := make(map[string]*int)
for len(queue) > 0 {
// 取出队首节点
nodeID := queue[0]
queue = queue[1:]
// 检查是否已执行
if executed[nodeID] {
continue
}
node, exists := nodeMap[nodeID]
if !exists {
continue
}
// 执行节点
nodeResult, nodeOutput := e.executeNode(node, wfCtx)
result.NodeResults = append(result.NodeResults, nodeResult)
if nodeOutput != nil && nodeOutput.Stream && nodeOutput.StreamChan != nil {
// 流式输出节点通常是最后一个节点
// 直接传递 StreamChan 给 WorkflowResult,不阻塞等待
result.Stream = true
result.StreamChan = nodeOutput.StreamChan
result.Event = wfCtx.Event
result.Status = "streaming"
result.Message = fmt.Sprintf("streaming output from node: %s", node.Name)
// 更新节点状态为 streaming
nodeResult.Status = "streaming"
nodeResult.Message = "streaming in progress"
// 立即返回,让 API 层处理流式响应
return result
}
executed[nodeID] = true
// 保存分支结果
if nodeResult.BranchIndex != nil {
branchResults[nodeID] = nodeResult.BranchIndex
}
// 检查执行状态
if nodeResult.Status == "failed" {
if !node.ContinueOnFail {
result.Status = models.ExecutionStatusFailed
result.ErrorNode = nodeID
result.Message = fmt.Sprintf("node %s failed: %s", node.Name, nodeResult.Error)
}
}
// 检查是否终止
if nodeResult.Status == "terminated" {
result.Message = fmt.Sprintf("workflow terminated at node %s", node.Name)
return result
}
// 更新后继节点的入度
if nodeConns, ok := connections[nodeID]; ok {
for outputIndex, targets := range nodeConns.Main {
// 检查是否应该走这个分支
if !e.shouldFollowBranch(nodeID, outputIndex, branchResults) {
continue
}
for _, target := range targets {
inDegree[target.Node]--
if inDegree[target.Node] == 0 {
queue = append(queue, target.Node)
}
}
}
}
}
return result
}
// executeNode 执行单个节点
// 返回:节点执行结果、节点输出(用于流式输出检测)
func (e *WorkflowEngine) executeNode(node *models.WorkflowNode, wfCtx *models.WorkflowContext) (*models.NodeExecutionResult, *models.NodeOutput) {
startTime := time.Now()
nodeResult := &models.NodeExecutionResult{
NodeID: node.ID,
NodeName: node.Name,
NodeType: node.Type,
StartedAt: startTime.Unix(),
}
var nodeOutput *models.NodeOutput
// 跳过禁用的节点
if node.Disabled {
nodeResult.Status = "skipped"
nodeResult.Message = "node is disabled"
nodeResult.FinishedAt = time.Now().Unix()
nodeResult.DurationMs = time.Since(startTime).Milliseconds()
return nodeResult, nil
}
// 获取处理器
processor, err := models.GetProcessorByType(node.Type, node.Config)
if err != nil {
nodeResult.Status = "failed"
nodeResult.Error = fmt.Sprintf("failed to get processor: %v", err)
nodeResult.FinishedAt = time.Now().Unix()
nodeResult.DurationMs = time.Since(startTime).Milliseconds()
return nodeResult, nil
}
// 执行处理器(带重试)
var retries int
maxRetries := node.MaxRetries
if !node.RetryOnFail {
maxRetries = 0
}
for retries <= maxRetries {
// 检查是否为分支处理器
if branchProcessor, ok := processor.(models.BranchProcessor); ok {
output, err := branchProcessor.ProcessWithBranch(e.ctx, wfCtx)
if err != nil {
if retries < maxRetries {
retries++
time.Sleep(time.Duration(node.RetryInterval) * time.Second)
continue
}
nodeResult.Status = "failed"
nodeResult.Error = err.Error()
} else {
nodeResult.Status = "success"
if output != nil {
nodeOutput = output
if output.WfCtx != nil {
wfCtx = output.WfCtx
}
nodeResult.Message = output.Message
nodeResult.BranchIndex = output.BranchIndex
if output.Terminate {
nodeResult.Status = "terminated"
}
}
}
break
}
// 普通处理器
newWfCtx, msg, err := processor.Process(e.ctx, wfCtx)
if err != nil {
if retries < maxRetries {
retries++
time.Sleep(time.Duration(node.RetryInterval) * time.Second)
continue
}
nodeResult.Status = "failed"
nodeResult.Error = err.Error()
} else {
nodeResult.Status = "success"
nodeResult.Message = msg
if newWfCtx != nil {
wfCtx = newWfCtx
// 检测流式输出标记
if newWfCtx.Stream && newWfCtx.StreamChan != nil {
nodeOutput = &models.NodeOutput{
WfCtx: newWfCtx,
Message: msg,
Stream: true,
StreamChan: newWfCtx.StreamChan,
}
}
}
// 如果事件被 drop(返回 nil 或 Event 为 nil),标记为终止
if newWfCtx == nil || newWfCtx.Event == nil {
nodeResult.Status = "terminated"
nodeResult.Message = msg
}
}
break
}
nodeResult.FinishedAt = time.Now().Unix()
nodeResult.DurationMs = time.Since(startTime).Milliseconds()
logger.Infof("workflow: executed node %s (type=%s) status=%s msg=%s duration=%dms",
node.Name, node.Type, nodeResult.Status, nodeResult.Message, nodeResult.DurationMs)
return nodeResult, nodeOutput
}
// shouldFollowBranch 判断是否应该走某个分支
func (e *WorkflowEngine) shouldFollowBranch(nodeID string, outputIndex int, branchResults map[string]*int) bool {
branchIndex, hasBranch := branchResults[nodeID]
if !hasBranch {
// 没有分支结果,说明不是分支节点,只走第一个输出
return outputIndex == 0
}
if branchIndex == nil {
// branchIndex 为 nil,走默认分支(通常是最后一个)
return true
}
// 只走选中的分支
return outputIndex == *branchIndex
}
func (e *WorkflowEngine) saveExecutionRecord(pipeline *models.EventPipeline, wfCtx *models.WorkflowContext, result *models.WorkflowResult, triggerCtx *models.WorkflowTriggerContext, startTime int64, duration int64) {
executionID := triggerCtx.RequestID
if executionID == "" {
executionID = uuid.New().String()
}
execution := &models.EventPipelineExecution{
ID: executionID,
PipelineID: pipeline.ID,
PipelineName: pipeline.Name,
Mode: triggerCtx.Mode,
Status: result.Status,
ErrorMessage: result.Message,
ErrorNode: result.ErrorNode,
CreatedAt: startTime,
FinishedAt: time.Now().Unix(),
DurationMs: duration,
TriggerBy: triggerCtx.TriggerBy,
}
if wfCtx.Event != nil {
execution.EventID = wfCtx.Event.Id
}
if err := execution.SetNodeResults(result.NodeResults); err != nil {
logger.Errorf("workflow: failed to set node results: pipeline_id=%d, error=%v", pipeline.ID, err)
}
if err := execution.SetInputsSnapshot(wfCtx.Inputs); err != nil {
logger.Errorf("workflow: failed to set inputs snapshot: pipeline_id=%d, error=%v", pipeline.ID, err)
}
if err := models.CreateEventPipelineExecution(e.ctx, execution); err != nil {
logger.Errorf("workflow: failed to save execution record: pipeline_id=%d, error=%v", pipeline.ID, err)
}
}
================================================
FILE: alert/pipeline/pipeline.go
================================================
package pipeline
import (
_ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/aisummary"
_ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/callback"
_ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/eventdrop"
_ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/eventupdate"
_ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/logic"
_ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/relabel"
)
func Init() {
}
================================================
FILE: alert/pipeline/processor/aisummary/ai_summary.go
================================================
package aisummary
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"text/template"
"time"
"github.com/ccfos/nightingale/v6/alert/pipeline/processor/callback"
"github.com/ccfos/nightingale/v6/alert/pipeline/processor/common"
"github.com/ccfos/nightingale/v6/models"
"github.com/ccfos/nightingale/v6/pkg/ctx"
"github.com/ccfos/nightingale/v6/pkg/tplx"
)
const (
HTTP_STATUS_SUCCESS_MAX = 299
)
// AISummaryConfig 配置结构体
type AISummaryConfig struct {
callback.HTTPConfig
ModelName string `json:"model_name"`
APIKey string `json:"api_key"`
PromptTemplate string `json:"prompt_template"`
CustomParams map[string]interface{} `json:"custom_params"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatCompletionResponse struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
}
func init() {
models.RegisterProcessor("ai_summary", &AISummaryConfig{})
}
func (c *AISummaryConfig) Init(settings interface{}) (models.Processor, error) {
result, err := common.InitProcessor[*AISummaryConfig](settings)
return result, err
}
func (c *AISummaryConfig) Process(ctx *ctx.Context, wfCtx *models.WorkflowContext) (*models.WorkflowContext, string, error) {
event := wfCtx.Event
if c.Client == nil {
if err := c.initHTTPClient(); err != nil {
return wfCtx, "", fmt.Errorf("failed to initialize HTTP client: %v processor: %v", err, c)
}
}
// 准备告警事件信息
eventInfo, err := c.prepareEventInfo(wfCtx)
if err != nil {
return wfCtx, "", fmt.Errorf("failed to prepare event info: %v processor: %v", err, c)
}
// 调用AI模型生成总结
summary, err := c.generateAISummary(eventInfo)
if err != nil {
return wfCtx, "", fmt.Errorf("failed to generate AI summary: %v processor: %v", err, c)
}
// 将总结添加到annotations字段
if event.AnnotationsJSON == nil {
event.AnnotationsJSON = make(map[string]string)
}
event.AnnotationsJSON["ai_summary"] = summary
// 更新Annotations字段
b, err := json.Marshal(event.AnnotationsJSON)
if err != nil {
return wfCtx, "", fmt.Errorf("failed to marshal annotations: %v processor: %v", err, c)
}
event.Annotations = string(b)
return wfCtx, "", nil
}
func (c *AISummaryConfig) initHTTPClient() error {
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipSSLVerify},
}
if c.Proxy != "" {
proxyURL, err := url.Parse(c.Proxy)
if err != nil {
return
gitextract_wwt_h4rf/
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── config.yml
│ │ ├── enhancement.md
│ │ └── question.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── issue-translator.yml
│ └── n9e.yml
├── .gitignore
├── .goreleaser.yaml
├── .typos.toml
├── LICENSE
├── Makefile
├── README.md
├── README_zh.md
├── alert/
│ ├── aconf/
│ │ └── conf.go
│ ├── alert.go
│ ├── astats/
│ │ └── stats.go
│ ├── common/
│ │ └── key.go
│ ├── dispatch/
│ │ ├── consume.go
│ │ ├── dispatch.go
│ │ ├── log.go
│ │ ├── notify_channel.go
│ │ └── notify_target.go
│ ├── eval/
│ │ ├── alert_rule.go
│ │ ├── eval.go
│ │ └── eval_test.go
│ ├── mute/
│ │ └── mute.go
│ ├── naming/
│ │ ├── hashring.go
│ │ ├── heartbeat.go
│ │ └── leader.go
│ ├── pipeline/
│ │ ├── engine/
│ │ │ └── engine.go
│ │ ├── pipeline.go
│ │ └── processor/
│ │ ├── aisummary/
│ │ │ ├── ai_summary.go
│ │ │ └── ai_summary_test.go
│ │ ├── callback/
│ │ │ └── callback.go
│ │ ├── common/
│ │ │ └── common.go
│ │ ├── eventdrop/
│ │ │ └── event_drop.go
│ │ ├── eventupdate/
│ │ │ └── event_update.go
│ │ ├── logic/
│ │ │ ├── if.go
│ │ │ └── switch.go
│ │ ├── relabel/
│ │ │ └── relabel.go
│ │ └── utils/
│ │ └── utils.go
│ ├── process/
│ │ ├── alert_cur_event.go
│ │ └── process.go
│ ├── queue/
│ │ └── queue.go
│ ├── record/
│ │ ├── prom_rule.go
│ │ ├── sample.go
│ │ └── scheduler.go
│ ├── router/
│ │ ├── router.go
│ │ ├── router_alert_eval_detail.go
│ │ ├── router_event.go
│ │ ├── router_event_detail.go
│ │ └── router_trace_logs.go
│ └── sender/
│ ├── callback.go
│ ├── dingtalk.go
│ ├── email.go
│ ├── feishu.go
│ ├── feishucard.go
│ ├── global_webhook.go
│ ├── global_webhook_test.go
│ ├── ibex.go
│ ├── lark.go
│ ├── larkcard.go
│ ├── mm.go
│ ├── notify_record_queue.go
│ ├── plugin.go
│ ├── plugin_cmd_unix.go
│ ├── plugin_cmd_windows.go
│ ├── sender.go
│ ├── telegram.go
│ ├── webhook.go
│ ├── webhook_event_queue.go
│ ├── webhook_event_queue_test.go
│ ├── webhook_queue.go
│ └── wecom.go
├── center/
│ ├── cconf/
│ │ ├── conf.go
│ │ ├── event_example.go
│ │ ├── metric.go
│ │ ├── ops.go
│ │ ├── plugin.go
│ │ ├── rsa/
│ │ │ └── rsa_conf.go
│ │ └── sql_tpl.go
│ ├── center.go
│ ├── cstats/
│ │ └── stats.go
│ ├── integration/
│ │ └── init.go
│ ├── metas/
│ │ └── metas.go
│ ├── router/
│ │ ├── router.go
│ │ ├── router_alert_aggr_view.go
│ │ ├── router_alert_cur_event.go
│ │ ├── router_alert_eval_detail.go
│ │ ├── router_alert_his_event.go
│ │ ├── router_alert_rule.go
│ │ ├── router_alert_subscribe.go
│ │ ├── router_board.go
│ │ ├── router_builtin.go
│ │ ├── router_builtin_component.go
│ │ ├── router_builtin_metric_filter.go
│ │ ├── router_builtin_metrics.go
│ │ ├── router_builtin_payload.go
│ │ ├── router_busi_group.go
│ │ ├── router_captcha.go
│ │ ├── router_chart_share.go
│ │ ├── router_config.go
│ │ ├── router_configs.go
│ │ ├── router_crypto.go
│ │ ├── router_dash_annotation.go
│ │ ├── router_dashboard.go
│ │ ├── router_datasource.go
│ │ ├── router_datasource_db.go
│ │ ├── router_embedded.go
│ │ ├── router_es.go
│ │ ├── router_es_index_pattern.go
│ │ ├── router_event_detail.go
│ │ ├── router_event_pipeline.go
│ │ ├── router_funcs.go
│ │ ├── router_heartbeat.go
│ │ ├── router_login.go
│ │ ├── router_message_template.go
│ │ ├── router_metric_desc.go
│ │ ├── router_metric_view.go
│ │ ├── router_mute.go
│ │ ├── router_mw.go
│ │ ├── router_notification_record.go
│ │ ├── router_notify_channel.go
│ │ ├── router_notify_channel_test.go
│ │ ├── router_notify_config.go
│ │ ├── router_notify_rule.go
│ │ ├── router_notify_tpl.go
│ │ ├── router_opensearch.go
│ │ ├── router_proxy.go
│ │ ├── router_query.go
│ │ ├── router_recording_rule.go
│ │ ├── router_role.go
│ │ ├── router_role_operation.go
│ │ ├── router_saved_view.go
│ │ ├── router_self.go
│ │ ├── router_server.go
│ │ ├── router_source_token.go
│ │ ├── router_target.go
│ │ ├── router_task.go
│ │ ├── router_task_tpl.go
│ │ ├── router_tdengine.go
│ │ ├── router_trace_logs.go
│ │ ├── router_user.go
│ │ ├── router_user_group.go
│ │ └── router_user_variable_config.go
│ └── sso/
│ ├── init.go
│ └── sync.go
├── cli/
│ ├── cli.go
│ └── upgrade/
│ ├── config.go
│ ├── readme.md
│ ├── upgrade.go
│ └── upgrade.sql
├── cmd/
│ ├── alert/
│ │ └── main.go
│ ├── center/
│ │ └── main.go
│ ├── cli/
│ │ └── main.go
│ ├── edge/
│ │ ├── edge.go
│ │ └── main.go
│ └── pushgw/
│ └── main.go
├── conf/
│ ├── conf.go
│ └── crypto.go
├── cron/
│ ├── clean_notify_record.go
│ └── clean_pipeline_execution.go
├── datasource/
│ ├── ck/
│ │ └── clickhouse.go
│ ├── commons/
│ │ └── eslike/
│ │ └── eslike.go
│ ├── datasource.go
│ ├── doris/
│ │ └── doris.go
│ ├── es/
│ │ └── es.go
│ ├── mysql/
│ │ └── mysql.go
│ ├── opensearch/
│ │ └── opensearch.go
│ ├── postgresql/
│ │ └── postgresql.go
│ ├── prom/
│ │ └── prom.go
│ ├── tdengine/
│ │ └── tdengine.go
│ └── victorialogs/
│ └── victorialogs.go
├── doc/
│ ├── README.bak.md
│ ├── active-contributors.md
│ ├── committers.md
│ ├── community-governance.md
│ ├── contributors.md
│ ├── end-users.md
│ ├── pmc.md
│ └── server-dash.json
├── docker/
│ ├── .dockerignore
│ ├── Dockerfile.goreleaser
│ ├── Dockerfile.goreleaser.arm64
│ ├── build.sh
│ ├── compose-bridge/
│ │ ├── docker-compose.yaml
│ │ ├── etc-categraf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.mysql/
│ │ │ │ └── mysql.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ ├── input.prometheus/
│ │ │ │ └── prometheus.toml
│ │ │ ├── input.redis/
│ │ │ │ └── redis.toml
│ │ │ └── input.system/
│ │ │ └── system.toml
│ │ ├── etc-mysql/
│ │ │ └── my.cnf
│ │ └── etc-nightingale/
│ │ ├── config.toml
│ │ ├── metrics.yaml
│ │ └── script/
│ │ ├── notify.bak.py
│ │ ├── notify.py
│ │ ├── notify_feishu.py
│ │ └── rule_converter.py
│ ├── compose-host-network/
│ │ ├── docker-compose.yaml
│ │ ├── etc-categraf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ └── input.system/
│ │ │ └── system.toml
│ │ ├── etc-mysql/
│ │ │ └── my.cnf
│ │ ├── etc-nightingale/
│ │ │ ├── config.toml
│ │ │ ├── metrics.yaml
│ │ │ └── script/
│ │ │ ├── notify.bak.py
│ │ │ ├── notify.py
│ │ │ ├── notify_feishu.py
│ │ │ └── rule_converter.py
│ │ └── etc-prometheus/
│ │ └── prometheus.yml
│ ├── compose-host-network-metric-log/
│ │ ├── docker-compose.yaml
│ │ ├── etc-categraf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ ├── input.system/
│ │ │ │ └── system.toml
│ │ │ └── logs.toml
│ │ ├── etc-logstash/
│ │ │ └── logstash.yaml
│ │ ├── etc-mysql/
│ │ │ └── my.cnf
│ │ ├── etc-nightingale/
│ │ │ ├── config.toml
│ │ │ ├── metrics.yaml
│ │ │ └── script/
│ │ │ ├── notify.bak.py
│ │ │ ├── notify.py
│ │ │ ├── notify_feishu.py
│ │ │ └── rule_converter.py
│ │ └── etc-prometheus/
│ │ └── prometheus.yml
│ ├── compose-postgres/
│ │ ├── categraf/
│ │ │ └── conf/
│ │ │ ├── config.toml
│ │ │ ├── input.cpu/
│ │ │ │ └── cpu.toml
│ │ │ ├── input.disk/
│ │ │ │ └── disk.toml
│ │ │ ├── input.diskio/
│ │ │ │ └── diskio.toml
│ │ │ ├── input.docker/
│ │ │ │ └── docker.toml
│ │ │ ├── input.kernel/
│ │ │ │ └── kernel.toml
│ │ │ ├── input.mem/
│ │ │ │ └── mem.toml
│ │ │ ├── input.net/
│ │ │ │ └── net.toml
│ │ │ ├── input.netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── input.processes/
│ │ │ │ └── processes.toml
│ │ │ ├── input.system/
│ │ │ │ └── system.toml
│ │ │ └── prometheus.toml
│ │ ├── docker-compose.yaml
│ │ ├── initsql_for_postgres/
│ │ │ ├── a-n9e-for-Postgres.sql
│ │ │ └── b-ibex-for-Postgres.sql
│ │ ├── n9eetc_pg/
│ │ │ ├── config.toml
│ │ │ └── metrics.yaml
│ │ └── prometc_vm/
│ │ ├── prometheus.yml
│ │ └── targets.json
│ ├── initsql/
│ │ ├── a-n9e.sql
│ │ └── c-init.sql
│ ├── migratesql/
│ │ └── migrate.sql
│ └── sqlite.sql
├── dscache/
│ ├── cache.go
│ └── sync.go
├── dskit/
│ ├── clickhouse/
│ │ ├── clickhouse.go
│ │ ├── clickhouse_test.go
│ │ └── timeseries.go
│ ├── doris/
│ │ ├── doris.go
│ │ ├── logs.go
│ │ ├── sql_analyzer.go
│ │ ├── sql_analyzer_test.go
│ │ ├── template.md
│ │ └── timeseries.go
│ ├── mysql/
│ │ ├── mysql.go
│ │ ├── mysql_test.go
│ │ ├── timeseries.go
│ │ └── timeseries_test.go
│ ├── pool/
│ │ └── pool.go
│ ├── postgres/
│ │ ├── postgres.go
│ │ └── timeseries.go
│ ├── sqlbase/
│ │ ├── base.go
│ │ ├── timeseries.go
│ │ └── timeseries_test.go
│ ├── tdengine/
│ │ └── tdengine.go
│ ├── types/
│ │ ├── timeseries.go
│ │ └── types.go
│ └── victorialogs/
│ ├── victorialogs.go
│ └── victorialogs_test.go
├── dumper/
│ ├── dumper.go
│ └── sync.go
├── etc/
│ ├── config.toml
│ ├── edge/
│ │ └── edge.toml
│ ├── metrics.yaml
│ └── script/
│ ├── notify.bak.py
│ ├── notify.py
│ ├── notify_feishu.py
│ └── rule_converter.py
├── fe.sh
├── go.mod
├── go.sum
├── integrations/
│ ├── AMD_ROCm_SMI/
│ │ ├── collect/
│ │ │ └── amd_rocm_smi/
│ │ │ └── rocm.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── AliYun/
│ │ ├── collect/
│ │ │ └── aliyun/
│ │ │ └── cloud.toml
│ │ ├── dashboards/
│ │ │ ├── arms-api.json
│ │ │ ├── arms-application.json
│ │ │ ├── arms-db.json
│ │ │ ├── arms-jvm-service.json
│ │ │ ├── arms-machine.json
│ │ │ ├── arms_jvm.json
│ │ │ ├── cdn.json
│ │ │ ├── ecs.json
│ │ │ ├── mongodb.json
│ │ │ ├── mse.json
│ │ │ ├── mysql.json
│ │ │ ├── nat.json
│ │ │ ├── oss.json
│ │ │ ├── polardb_mysql.json
│ │ │ ├── rds.json
│ │ │ ├── rds_new.json
│ │ │ ├── redis.json
│ │ │ ├── redis_cluster.json
│ │ │ ├── redis_new.json
│ │ │ ├── redis_standard.json
│ │ │ ├── slb.json
│ │ │ ├── slb_new.json
│ │ │ └── waf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── AppDynamics/
│ │ ├── collect/
│ │ │ └── appdynamics/
│ │ │ └── app.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── AutoMQ/
│ │ ├── alerts/
│ │ │ └── 常用告警规则.json
│ │ ├── collect/
│ │ │ └── prometheus/
│ │ │ └── 采集OTEL-COLLECTOR的样例.toml
│ │ ├── dashboards/
│ │ │ ├── broker_metrics.json
│ │ │ ├── cluster_overview.json
│ │ │ ├── detailed_metrics.json
│ │ │ ├── group_metrics.json
│ │ │ └── topic_metrics.json
│ │ ├── markdown/
│ │ │ └── overview.md
│ │ └── metrics/
│ │ └── exporter.json
│ ├── Bind/
│ │ ├── collect/
│ │ │ └── bind/
│ │ │ └── bind.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Canal/
│ │ ├── dashboards/
│ │ │ └── canal_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Ceph/
│ │ ├── alerts/
│ │ │ └── ceph_by_categraf.json
│ │ ├── dashboards/
│ │ │ └── ceph_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── ClickHouse/
│ │ ├── alerts/
│ │ │ ├── clickhouse_by_categraf.json
│ │ │ └── clickhouse_by_exporter.json
│ │ ├── collect/
│ │ │ └── clickhouse/
│ │ │ └── clickhouse.toml
│ │ ├── dashboards/
│ │ │ ├── clickhouse_by_categraf.json
│ │ │ └── clickhouse_by_exporter.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ ├── clickhouse_by_categraf.json
│ │ └── clickhouse_by_exporter.json
│ ├── CloudWatch/
│ │ ├── collect/
│ │ │ └── cloudwatch/
│ │ │ └── cloud.toml
│ │ ├── dashboards/
│ │ │ └── dashboard-by-aws-rds.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Consul/
│ │ ├── collect/
│ │ │ └── consul/
│ │ │ └── consul.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Dns_Query/
│ │ ├── collect/
│ │ │ └── dns_query/
│ │ │ └── dns_query.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Docker/
│ │ ├── collect/
│ │ │ └── docker/
│ │ │ └── docker.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Doris/
│ │ ├── alerts/
│ │ │ └── doris_by_categraf.json
│ │ ├── collect/
│ │ │ └── prometheus/
│ │ │ └── collect_doris_examples.toml
│ │ ├── dashboards/
│ │ │ └── Doris_Overview.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Elasticsearch/
│ │ ├── alerts/
│ │ │ ├── elasticsearch_by_categraf.json
│ │ │ └── elasticsearch_by_exporter.json
│ │ ├── collect/
│ │ │ └── elasticsearch/
│ │ │ └── elasticsearch.toml
│ │ ├── dashboards/
│ │ │ ├── elasticsearch_by_categraf.json
│ │ │ ├── elasticsearch_by_categraf_0.3.102.json
│ │ │ ├── elasticsearch_by_categraf_a.json
│ │ │ ├── elasticsearch_by_categraf_b.json
│ │ │ └── elasticsearch_by_exporter.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf-base.json
│ ├── Exec/
│ │ ├── collect/
│ │ │ └── exec/
│ │ │ └── exec.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Filecount/
│ │ ├── collect/
│ │ │ └── filecount/
│ │ │ └── filecount.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Gitlab/
│ │ ├── alerts/
│ │ │ └── gitlab_by_categraf.json
│ │ ├── dashboards/
│ │ │ ├── MachinePerformance.json
│ │ │ ├── NGINXVTS.json
│ │ │ ├── Overview.json
│ │ │ ├── PostgreSQL.json
│ │ │ └── Redis.json
│ │ └── markdown/
│ │ └── README.md
│ ├── GoogleCloud/
│ │ ├── collect/
│ │ │ └── googlecloud/
│ │ │ └── gcp.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── HAProxy/
│ │ ├── collect/
│ │ │ └── haproxy/
│ │ │ └── haproxy.toml
│ │ ├── dashboards/
│ │ │ └── dashboard.json
│ │ └── markdown/
│ │ └── README.md
│ ├── HTTP_Response/
│ │ ├── alerts/
│ │ │ └── http_response_by_categraf.json
│ │ ├── collect/
│ │ │ └── http_response/
│ │ │ └── http_response.toml
│ │ ├── dashboards/
│ │ │ └── http_response_by_categraf.json
│ │ ├── markdown/
│ │ │ └── http.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── IPMI/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── collect/
│ │ │ └── ipmi/
│ │ │ └── conf.toml
│ │ ├── dashboards/
│ │ │ ├── IPMI.json
│ │ │ ├── IPMI_by_categraf.json
│ │ │ └── IPMI_by_prometheus.json
│ │ └── markdown/
│ │ └── README.md
│ ├── IPVS/
│ │ ├── collect/
│ │ │ └── ipvs/
│ │ │ └── ipvs.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Java/
│ │ └── dashboards/
│ │ ├── jmx_by_exporter.json
│ │ ├── jmx_by_kubernetes.json
│ │ └── jvm_by_opentelementry.json
│ ├── Jenkins/
│ │ ├── collect/
│ │ │ └── jenkins/
│ │ │ └── jenkins.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Jolokia_Agent/
│ │ ├── collect/
│ │ │ └── jolokia_agent/
│ │ │ ├── activemq.toml
│ │ │ ├── bitbucket.toml
│ │ │ ├── cassandra.toml
│ │ │ ├── hadoop-hdfs.toml
│ │ │ ├── java.toml
│ │ │ ├── jboss.toml
│ │ │ ├── kafka-connect.toml
│ │ │ ├── kafka.toml
│ │ │ ├── tomcat.toml
│ │ │ ├── weblogic.toml
│ │ │ └── zookeeper.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Kafka/
│ │ ├── alerts/
│ │ │ ├── kafka_by_categraf.json
│ │ │ └── kafka_by_exporter.json
│ │ ├── collect/
│ │ │ └── kafka/
│ │ │ └── kafka.toml
│ │ ├── dashboards/
│ │ │ ├── kafka_by_categraf.json
│ │ │ └── kafka_by_exporter.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf-base.json
│ ├── Kubernetes/
│ │ ├── alerts/
│ │ │ ├── apiserver.json
│ │ │ ├── kube-controller-plane.json
│ │ │ ├── kubelet.json
│ │ │ ├── node-exporter.json
│ │ │ ├── prometheus-operator.json
│ │ │ └── prometheus.json
│ │ ├── dashboards/
│ │ │ ├── APIServer.json
│ │ │ ├── ControllerManager.json
│ │ │ ├── DeploymentContainer.json
│ │ │ ├── KubeStateMetrics.json
│ │ │ ├── KubeletMetrics.json
│ │ │ ├── Pod.json
│ │ │ ├── Scheduler.json
│ │ │ └── StatefulsetContainer.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ ├── metrics/
│ │ │ ├── k8s-node.json
│ │ │ └── k8s-pod.json
│ │ └── record-rules/
│ │ ├── kube-controller-plane.json
│ │ └── node-exporter.json
│ ├── Ldap/
│ │ ├── collect/
│ │ │ └── ldap/
│ │ │ └── ldap.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Linux/
│ │ ├── alerts/
│ │ │ ├── CommonAlertingRules-Categraf.json
│ │ │ ├── linux_by_categraf.json
│ │ │ ├── linux_by_exporter.json
│ │ │ ├── linux_by_telegraf.json
│ │ │ └── 常用中文告警规则-采集器Categraf.json
│ │ ├── collect/
│ │ │ ├── arp_packet/
│ │ │ │ └── arp_packet.toml
│ │ │ ├── kernel_vmstat/
│ │ │ │ └── kernel_vmstat.toml
│ │ │ ├── netstat/
│ │ │ │ └── netstat.toml
│ │ │ ├── ntp/
│ │ │ │ └── ntp.toml
│ │ │ └── processes/
│ │ │ └── processes.toml
│ │ ├── dashboards/
│ │ │ ├── categraf-detail.json
│ │ │ ├── categraf-overview.json
│ │ │ ├── categraf-processes.json
│ │ │ ├── categraf-table-ng.json
│ │ │ └── exporter-detail.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ ├── categraf-base.json
│ │ └── exporter-base.json
│ ├── Logstash/
│ │ ├── collect/
│ │ │ └── logstash/
│ │ │ └── logstash.toml
│ │ ├── dashboards/
│ │ │ └── logstash-dash.json
│ │ └── markdown/
│ │ └── README.md
│ ├── MinIO/
│ │ ├── alerts/
│ │ │ └── minio_by_categraf.json
│ │ ├── dashboards/
│ │ │ ├── minio_by_categraf.json
│ │ │ └── new-version.json
│ │ └── markdown/
│ │ └── README.md
│ ├── MongoDB/
│ │ ├── alerts/
│ │ │ └── mongo_by_exporter.json
│ │ ├── collect/
│ │ │ └── mongodb/
│ │ │ └── mongodb.toml
│ │ ├── dashboards/
│ │ │ └── mongo_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Mtail/
│ │ ├── collect/
│ │ │ └── mtail/
│ │ │ └── mtail.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── MySQL/
│ │ ├── alerts/
│ │ │ ├── mysql_by_categraf.json
│ │ │ └── mysql_by_exporter.json
│ │ ├── collect/
│ │ │ └── mysql/
│ │ │ └── mysql.toml
│ │ ├── dashboards/
│ │ │ ├── MySQL-by-address.json
│ │ │ ├── MySQL仪表盘-远端.json
│ │ │ ├── MySQL仪表盘.json
│ │ │ ├── mysql_by_categraf.json
│ │ │ ├── mysql_by_categraf_ident.json
│ │ │ ├── mysql_by_categraf_instance.json
│ │ │ └── mysql_by_exporter.json
│ │ ├── markdown/
│ │ │ ├── README.md
│ │ │ └── mysql.md
│ │ └── metrics/
│ │ └── categraf-base.json
│ ├── N9E/
│ │ ├── dashboards/
│ │ │ ├── n9e_server.json
│ │ │ ├── n9e_v6.json
│ │ │ └── n9e_v8.json
│ │ └── markdown/
│ │ └── README.md
│ ├── NFSClient/
│ │ ├── collect/
│ │ │ └── nfsclient/
│ │ │ └── nfsclient.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── NSQ/
│ │ ├── collect/
│ │ │ └── nsq/
│ │ │ └── nsq.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── NVIDIA/
│ │ ├── collect/
│ │ │ └── nvidia_smi/
│ │ │ └── nvidia_smi.toml
│ │ ├── dashboards/
│ │ │ └── nvidia-gpu-metrics-by-categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Net_Response/
│ │ ├── alerts/
│ │ │ └── net_response_by_categraf.json
│ │ ├── collect/
│ │ │ └── net_response/
│ │ │ └── net_response.toml
│ │ ├── dashboards/
│ │ │ ├── dashboard-by-ziv.json
│ │ │ └── net_response_by_categraf.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── Netstat_Filter/
│ │ ├── collect/
│ │ │ └── netstat_filter/
│ │ │ └── netstat_filter.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Nginx/
│ │ ├── collect/
│ │ │ ├── nginx/
│ │ │ │ └── nginx.toml
│ │ │ └── nginx_upstream_check/
│ │ │ └── nginx_upstream_check.toml
│ │ ├── dashboards/
│ │ │ ├── nginx_stub_status.json
│ │ │ ├── nginx_upstream_check.json
│ │ │ └── nginx_vts.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── Oracle/
│ │ ├── alerts/
│ │ │ └── oracle_alert.json
│ │ ├── collect/
│ │ │ └── oracle/
│ │ │ └── oracle.toml
│ │ ├── dashboards/
│ │ │ └── oracle_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── PHP/
│ │ ├── collect/
│ │ │ └── phpfpm/
│ │ │ └── phpfpm.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Ping/
│ │ ├── alerts/
│ │ │ └── ping_by_categraf.json
│ │ ├── collect/
│ │ │ └── ping/
│ │ │ └── ping.toml
│ │ ├── dashboards/
│ │ │ ├── ping_by_categraf_a.json
│ │ │ └── ping_by_categraf_b.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── PostgreSQL/
│ │ ├── alerts/
│ │ │ └── postgresql_by_categraf.json
│ │ ├── collect/
│ │ │ └── postgresql/
│ │ │ └── postgresql.toml
│ │ ├── dashboards/
│ │ │ └── postgresql_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Procstat/
│ │ ├── alerts/
│ │ │ └── categraf-procstat.json
│ │ ├── collect/
│ │ │ └── procstat/
│ │ │ └── procstat.toml
│ │ ├── dashboards/
│ │ │ └── categraf-procstat.json
│ │ ├── markdown/
│ │ │ └── readme.md
│ │ └── metrics/
│ │ └── categraf.json
│ ├── Prometheus/
│ │ ├── collect/
│ │ │ └── prometheus/
│ │ │ └── prometheus.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── RabbitMQ/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── collect/
│ │ │ └── rabbitmq/
│ │ │ └── rabbitmq.toml
│ │ ├── dashboards/
│ │ │ ├── rabbitmq_CN_v3.8_gt.json
│ │ │ ├── rabbitmq_by_categraf.json
│ │ │ ├── rabbitmq_v3.8_gt.json
│ │ │ └── rabbitmq_v3.8_lt.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Redis/
│ │ ├── alerts/
│ │ │ ├── redis_by_categraf.json
│ │ │ └── redis_by_exporter.json
│ │ ├── collect/
│ │ │ ├── redis/
│ │ │ │ └── redis.toml
│ │ │ └── redis_sentinel/
│ │ │ └── redis_sentinel.toml
│ │ ├── dashboards/
│ │ │ ├── FilterByAddress.json
│ │ │ ├── redis_by_categraf.json
│ │ │ └── redis_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SMART/
│ │ ├── collect/
│ │ │ └── smart/
│ │ │ └── smart.toml
│ │ ├── dashboards/
│ │ │ └── smart.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SNMP/
│ │ ├── collect/
│ │ │ └── snmp/
│ │ │ ├── Cisco.toml
│ │ │ ├── snmp.toml
│ │ │ └── snmp.toml.example
│ │ ├── dashboards/
│ │ │ ├── dashboards.json
│ │ │ ├── switch branch.json
│ │ │ └── switch main.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SQLServer/
│ │ ├── collect/
│ │ │ └── sqlserver/
│ │ │ └── sqlserver.toml
│ │ ├── dashboards/
│ │ │ └── sqlserver.json
│ │ └── markdown/
│ │ └── README.md
│ ├── SpringBoot/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── dashboards/
│ │ │ ├── JVM(Actuator)withapplicationname.json
│ │ │ └── JVM.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Switch_Legacy/
│ │ ├── collect/
│ │ │ └── switch_legacy/
│ │ │ └── switch_legacy.toml
│ │ ├── dashboards/
│ │ │ └── dashboard.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Systemd/
│ │ ├── collect/
│ │ │ └── systemd/
│ │ │ └── systemd.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── TDEngine/
│ │ ├── dashboards/
│ │ │ └── tasokeeper3.x.json
│ │ └── markdown/
│ │ └── README.md
│ ├── TiDB/
│ │ ├── alerts/
│ │ │ └── tidb-alerts.json
│ │ └── dashboards/
│ │ └── tidb-dashboard.json
│ ├── Tomcat/
│ │ ├── collect/
│ │ │ └── tomcat/
│ │ │ └── tomcat.toml
│ │ ├── dashboards/
│ │ │ └── tomcat_by_categraf.json
│ │ └── markdown/
│ │ └── README.md
│ ├── VictoriaMetrics/
│ │ ├── alerts/
│ │ │ └── alerts.json
│ │ ├── dashboards/
│ │ │ ├── victoriametrics-cluster.json
│ │ │ └── victoriametrics-single.json
│ │ └── markdown/
│ │ └── README.md
│ ├── Whois/
│ │ ├── collect/
│ │ │ └── whois/
│ │ │ └── whois.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── Windows/
│ │ ├── alerts/
│ │ │ ├── windows_by_categraf.json
│ │ │ └── windows_by_exporter.json
│ │ ├── dashboards/
│ │ │ ├── windows_by_categraf.json
│ │ │ └── windows_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── XSKYApi/
│ │ ├── collect/
│ │ │ └── xskyapi/
│ │ │ └── xskyapi.toml
│ │ └── markdown/
│ │ └── README.md
│ ├── ZooKeeper/
│ │ ├── alerts/
│ │ │ └── zookeeper_by_exporter.json
│ │ ├── collect/
│ │ │ └── zookeeper/
│ │ │ └── zookeeper.toml
│ │ ├── dashboards/
│ │ │ └── zookeeper_by_exporter.json
│ │ └── markdown/
│ │ └── README.md
│ ├── cAdvisor/
│ │ ├── collect/
│ │ │ └── cadvisor/
│ │ │ └── cadvisor.toml
│ │ ├── dashboards/
│ │ │ └── dashboard.json
│ │ ├── markdown/
│ │ │ └── README.md
│ │ └── metrics/
│ │ └── exporter-base.json
│ └── vSphere/
│ ├── alerts/
│ │ └── alerts.json
│ ├── collect/
│ │ └── vsphere/
│ │ └── vsphere.toml
│ ├── dashboards/
│ │ ├── vmware_by_vsphere-monitor.json
│ │ └── vsphere.json
│ └── markdown/
│ └── README.md
├── memsto/
│ ├── alert_mute_cache.go
│ ├── alert_rule_cache.go
│ ├── alert_subscribe_cache.go
│ ├── busi_group_cache.go
│ ├── config_cache.go
│ ├── config_cval_cache.go
│ ├── datasource_cache.go
│ ├── drop_ident.go
│ ├── es_index_pattern.go
│ ├── event_processor_cache.go
│ ├── host_alert_rule_targets.go
│ ├── memsto.go
│ ├── message_template_cache.go
│ ├── notify_channel_cache.go
│ ├── notify_config.go
│ ├── notify_rule_cache.go
│ ├── recording_rule_cache.go
│ ├── stat.go
│ ├── target_cache.go
│ ├── task_tpl_cache.go
│ ├── user_cache.go
│ ├── user_group_cache.go
│ └── user_token_cache.go
├── models/
│ ├── alert_aggr_view.go
│ ├── alert_cur_event.go
│ ├── alert_his_event.go
│ ├── alert_mute.go
│ ├── alert_rule.go
│ ├── alert_subscribe.go
│ ├── alerting_engine.go
│ ├── anomaly_point.go
│ ├── board.go
│ ├── board_busi.go
│ ├── board_payload.go
│ ├── builtin_cate.go
│ ├── builtin_component.go
│ ├── builtin_metrics.go
│ ├── builtin_metrics_filter.go
│ ├── builtin_payload.go
│ ├── busi_group.go
│ ├── busi_group_member.go
│ ├── chart.go
│ ├── chart_group.go
│ ├── chart_share.go
│ ├── common.go
│ ├── configs.go
│ ├── dash_annotation.go
│ ├── dashboard.go
│ ├── datasource.go
│ ├── embedded_product.go
│ ├── es_index_pattern.go
│ ├── event_pipeline.go
│ ├── event_pipeline_execution.go
│ ├── event_processor.go
│ ├── host_meta.go
│ ├── message_tpl.go
│ ├── metric_view.go
│ ├── migrate/
│ │ ├── migrate.go
│ │ ├── migrate_es_index_pattern.go
│ │ └── migrate_test.go
│ ├── notification_record.go
│ ├── notify_channel.go
│ ├── notify_channel_test.go
│ ├── notify_config.go
│ ├── notify_rule.go
│ ├── notify_tpl.go
│ ├── prom_alert_rule.go
│ ├── prom_alert_rule_test.go
│ ├── recording_rule.go
│ ├── role.go
│ ├── role_operation.go
│ ├── saved_view.go
│ ├── source_token.go
│ ├── sso_config.go
│ ├── target.go
│ ├── target_busi_group.go
│ ├── task_record.go
│ ├── task_tpl.go
│ ├── ts.go
│ ├── user.go
│ ├── user_group.go
│ ├── user_group_member.go
│ ├── user_token.go
│ └── workflow.go
├── pkg/
│ ├── aop/
│ │ ├── log.go
│ │ └── rec.go
│ ├── cas/
│ │ └── cas.go
│ ├── cfg/
│ │ ├── cfg.go
│ │ └── scan.go
│ ├── choice/
│ │ └── choice.go
│ ├── cmdx/
│ │ ├── cmd_notwindows.go
│ │ ├── cmd_windows.go
│ │ └── cmdx.go
│ ├── ctx/
│ │ └── ctx.go
│ ├── dingtalk/
│ │ ├── dingtalk.go
│ │ └── user/
│ │ └── client.go
│ ├── fasttime/
│ │ └── fasttime.go
│ ├── feishu/
│ │ └── feishu.go
│ ├── flashduty/
│ │ ├── post.go
│ │ ├── sync_user.go
│ │ ├── sync_user_group.go
│ │ └── sync_user_test.go
│ ├── ginx/
│ │ ├── auth.go
│ │ ├── bytesconv.go
│ │ ├── errorx.go
│ │ ├── funcs.go
│ │ ├── param.go
│ │ └── render.go
│ ├── hash/
│ │ ├── hash.go
│ │ ├── hash_fnv.go
│ │ └── hash_md5.go
│ ├── httpx/
│ │ └── httpx.go
│ ├── i18nx/
│ │ ├── i18n.go
│ │ └── var.go
│ ├── ibex/
│ │ └── ibex.go
│ ├── ldapx/
│ │ ├── ldapx.go
│ │ └── user_sync.go
│ ├── loggrep/
│ │ └── loggrep.go
│ ├── logx/
│ │ └── logx.go
│ ├── macros/
│ │ └── macros.go
│ ├── oauth2x/
│ │ └── oauth2x.go
│ ├── oidcx/
│ │ └── oidc.go
│ ├── ormx/
│ │ ├── database_init.go
│ │ ├── database_init_test.go
│ │ ├── ormx.go
│ │ └── types.go
│ ├── osx/
│ │ └── osx.go
│ ├── parser/
│ │ ├── calc.go
│ │ └── calc_test.go
│ ├── poster/
│ │ ├── post.go
│ │ └── post_test.go
│ ├── prom/
│ │ ├── client_option.go
│ │ ├── conv.go
│ │ ├── conv_test.go
│ │ ├── reader.go
│ │ └── writer.go
│ ├── promql/
│ │ ├── parser.go
│ │ ├── perser_test.go
│ │ └── promql.go
│ ├── secu/
│ │ ├── aes.go
│ │ └── rsa.go
│ ├── slice/
│ │ └── contains.go
│ ├── strx/
│ │ └── verify.go
│ ├── tlsx/
│ │ ├── common.go
│ │ └── config.go
│ ├── tplx/
│ │ ├── conv.go
│ │ ├── fns.go
│ │ ├── tpl_test.go
│ │ └── tplx.go
│ ├── unit/
│ │ ├── unit_convert.go
│ │ └── unit_convert_test.go
│ └── version/
│ └── version.go
├── prom/
│ ├── client.go
│ ├── option.go
│ └── reader.go
├── pushgw/
│ ├── idents/
│ │ └── idents.go
│ ├── kafka/
│ │ └── producer.go
│ ├── pconf/
│ │ └── conf.go
│ ├── pstat/
│ │ └── pstat.go
│ ├── pushgw.go
│ ├── router/
│ │ ├── fns.go
│ │ ├── router.go
│ │ ├── router_datadog.go
│ │ ├── router_datadog_easyjson.go
│ │ ├── router_heartbeat.go
│ │ ├── router_openfalcon.go
│ │ ├── router_openfalcon_easyjson.go
│ │ ├── router_opentsdb.go
│ │ ├── router_opentsdb_easyjson.go
│ │ ├── router_proxy_remotewrite.go
│ │ ├── router_remotewrite.go
│ │ ├── router_target.go
│ │ └── vars.go
│ └── writer/
│ ├── kafka_writer.go
│ ├── queue.go
│ ├── relabel.go
│ ├── relabel_test.go
│ └── writer.go
└── storage/
├── redis.go
├── redis_test.go
└── storage.go
Showing preview only (395K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4509 symbols across 384 files)
FILE: alert/aconf/conf.go
type Alert (line 7) | type Alert struct
method PreCheck (line 61) | func (a *Alert) PreCheck(configDir string) {
type SMTPConfig (line 14) | type SMTPConfig struct
type HeartbeatConfig (line 24) | type HeartbeatConfig struct
type Alerting (line 31) | type Alerting struct
type GlobalWebhook (line 39) | type GlobalWebhook struct
type CallPlugin (line 49) | type CallPlugin struct
type RedisPub (line 55) | type RedisPub struct
FILE: alert/alert.go
function Initialize (line 34) | func Initialize(configDir string, cryptoKey string) (func(), error) {
function Start (line 99) | func Start(alertc aconf.Alert, pushgwc pconf.Pushgw, syncStats *memsto.S...
FILE: alert/astats/stats.go
constant namespace (line 8) | namespace = "n9e"
constant subsystem (line 9) | subsystem = "alert"
type Stats (line 12) | type Stats struct
function NewSyncStats (line 32) | func NewSyncStats() *Stats {
FILE: alert/common/key.go
function RuleKey (line 11) | func RuleKey(datasourceId, id int64) string {
function MatchTags (line 15) | func MatchTags(eventTagsMap map[string]string, itags []models.TagFilter)...
function MatchGroupsName (line 41) | func MatchGroupsName(groupName string, groupFilter []models.TagFilter) b...
function matchTag (line 50) | func matchTag(value string, filter models.TagFilter) bool {
function targetGroupMatch (line 72) | func targetGroupMatch(value string, filter models.TagFilter) bool {
FILE: alert/dispatch/consume.go
type Consumer (line 25) | type Consumer struct
method LoopConsume (line 62) | func (e *Consumer) LoopConsume() {
method consume (line 75) | func (e *Consumer) consume(events []interface{}, sema *semaphore.Semap...
method consumeOne (line 90) | func (e *Consumer) consumeOne(event *models.AlertCurEvent) {
method persist (line 123) | func (e *Consumer) persist(event *models.AlertCurEvent) {
method queryRecoveryVal (line 146) | func (e *Consumer) queryRecoveryVal(event *models.AlertCurEvent) {
type EventMuteHookFunc (line 34) | type EventMuteHookFunc
function InitRegisterQueryFunc (line 38) | func InitRegisterQueryFunc(promClients *prom.PromClientMap) {
function NewConsumer (line 51) | func NewConsumer(alerting aconf.Alerting, ctx *ctx.Context, dispatch *Di...
FILE: alert/dispatch/dispatch.go
function init (line 34) | func init() {
type Dispatch (line 39) | type Dispatch struct
method ReloadTpls (line 106) | func (e *Dispatch) ReloadTpls() error {
method reloadTpls (line 121) | func (e *Dispatch) reloadTpls() error {
method HandleEventWithNotifyRule (line 167) | func (e *Dispatch) HandleEventWithNotifyRule(eventOrigin *models.Alert...
method HandleEventNotify (line 608) | func (e *Dispatch) HandleEventNotify(event *models.AlertCurEvent, isSu...
method handleSubs (line 658) | func (e *Dispatch) handleSubs(event *models.AlertCurEvent) {
method handleSub (line 676) | func (e *Dispatch) handleSub(sub *models.AlertSubscribe, event models....
method Send (line 725) | func (e *Dispatch) Send(rule *models.AlertRule, event *models.AlertCur...
method SendCallbacks (line 771) | func (e *Dispatch) SendCallbacks(rule *models.AlertRule, notifyTarget ...
method HandleIbex (line 824) | func (e *Dispatch) HandleIbex(rule *models.AlertRule, event *models.Al...
method genNoticeBytes (line 858) | func (e *Dispatch) genNoticeBytes(event *models.AlertCurEvent) []byte {
function NewDispatch (line 68) | func NewDispatch(alertRuleCache *memsto.AlertRuleCacheType, userCache *m...
function shouldSkipNotify (line 224) | func shouldSkipNotify(ctx *ctx.Context, event *models.AlertCurEvent, not...
function HandleEventPipeline (line 237) | func HandleEventPipeline(pipelineConfigs []models.PipelineConfig, eventO...
function PipelineApplicable (line 285) | func PipelineApplicable(pipeline *models.EventPipeline, event *models.Al...
function NotifyRuleMatchCheck (line 331) | func NotifyRuleMatchCheck(notifyConfig *models.NotifyConfig, event *mode...
function GetNotifyConfigParams (line 444) | func GetNotifyConfigParams(notifyConfig *models.NotifyConfig, contactKey...
function SendNotifyRuleMessage (line 523) | func SendNotifyRuleMessage(ctx *ctx.Context, userCache *memsto.UserCache...
function NeedBatchContacts (line 600) | func NeedBatchContacts(requestConfig *models.HTTPRequestConfig) bool {
type Notice (line 853) | type Notice struct
function fillUsers (line 884) | func fillUsers(ce *models.AlertCurEvent, uc *memsto.UserCacheType, ugc *...
function mapKeys (line 907) | func mapKeys(m map[int64]struct{}) []int64 {
function getSendTarget (line 915) | func getSendTarget(customParams map[string]string, sendtos []string) str...
FILE: alert/dispatch/log.go
function LogEvent (line 9) | func LogEvent(event *models.AlertCurEvent, location string, err ...error) {
FILE: alert/dispatch/notify_channel.go
type NotifyChannels (line 4) | type NotifyChannels
method OrMerge (line 14) | func (nc NotifyChannels) OrMerge(other NotifyChannels) {
method AndMerge (line 18) | func (nc NotifyChannels) AndMerge(other NotifyChannels) {
method merge (line 22) | func (nc NotifyChannels) merge(other NotifyChannels, f func(bool, bool...
function NewNotifyChannels (line 6) | func NewNotifyChannels(channels []string) NotifyChannels {
FILE: alert/dispatch/notify_target.go
type NotifyTarget (line 10) | type NotifyTarget struct
method OrMerge (line 25) | func (s *NotifyTarget) OrMerge(other *NotifyTarget) {
method AndMerge (line 35) | func (s *NotifyTarget) AndMerge(other *NotifyTarget) {
method merge (line 39) | func (s *NotifyTarget) merge(other *NotifyTarget, f func(NotifyChannel...
method ToChannelUserMap (line 59) | func (s *NotifyTarget) ToChannelUserMap() map[string][]int64 {
method ToCallbackList (line 71) | func (s *NotifyTarget) ToCallbackList() []string {
method ToWebhookMap (line 79) | func (s *NotifyTarget) ToWebhookMap() map[string]*models.Webhook {
method ToUidList (line 83) | func (s *NotifyTarget) ToUidList() []int64 {
function NewNotifyTarget (line 16) | func NewNotifyTarget() *NotifyTarget {
type NotifyTargetDispatch (line 95) | type NotifyTargetDispatch
function NotifyGroupDispatch (line 98) | func NotifyGroupDispatch(rule *models.AlertRule, event *models.AlertCurE...
function GlobalWebhookDispatch (line 118) | func GlobalWebhookDispatch(rule *models.AlertRule, event *models.AlertCu...
function EventCallbacksDispatch (line 130) | func EventCallbacksDispatch(rule *models.AlertRule, event *models.AlertC...
FILE: alert/eval/alert_rule.go
type Scheduler (line 20) | type Scheduler struct
method LoopSyncRules (line 72) | func (s *Scheduler) LoopSyncRules(ctx context.Context) {
method syncAlertRules (line 85) | func (s *Scheduler) syncAlertRules() {
function NewScheduler (line 43) | func NewScheduler(aconf aconf.Alert, externalProcessors *process.Externa...
FILE: alert/eval/eval.go
type AlertRuleWorker (line 38) | type AlertRuleWorker struct
method Key (line 128) | func (arw *AlertRuleWorker) Key() string {
method Hash (line 132) | func (arw *AlertRuleWorker) Hash() string {
method Prepare (line 141) | func (arw *AlertRuleWorker) Prepare() {
method Start (line 145) | func (arw *AlertRuleWorker) Start() {
method Eval (line 149) | func (arw *AlertRuleWorker) Eval() {
method Stop (line 238) | func (arw *AlertRuleWorker) Stop() {
method GetPromAnomalyPoint (line 246) | func (arw *AlertRuleWorker) GetPromAnomalyPoint(ruleConfig string) ([]...
method VarFillingAfterQuery (line 391) | func (arw *AlertRuleWorker) VarFillingAfterQuery(query models.PromQuer...
method getParamPermutation (line 569) | func (arw *AlertRuleWorker) getParamPermutation(paramVal map[string]mo...
method getHostIdents (line 685) | func (arw *AlertRuleWorker) getHostIdents(paramQuery models.ParamQuery...
method getDeviceIdents (line 727) | func (arw *AlertRuleWorker) getDeviceIdents(paramQuery models.ParamQue...
method GetHostAnomalyPoint (line 760) | func (arw *AlertRuleWorker) GetHostAnomalyPoint(ruleConfig string) ([]...
method VarFillingBeforeQuery (line 1233) | func (arw *AlertRuleWorker) VarFillingBeforeQuery(query models.PromQue...
method GetAnomalyPoint (line 1438) | func (arw *AlertRuleWorker) GetAnomalyPoint(rule *models.AlertRule, ds...
constant GET_RULE_CONFIG (line 60) | GET_RULE_CONFIG = "get_rule_config"
constant GET_Processor (line 61) | GET_Processor = "get_Processor"
constant CHECK_QUERY (line 62) | CHECK_QUERY = "check_query_config"
constant GET_CLIENT (line 63) | GET_CLIENT = "get_client"
constant QUERY_DATA (line 64) | QUERY_DATA = "query_data"
constant EXEC_TEMPLATE (line 65) | EXEC_TEMPLATE = "exec_template"
constant JoinMark (line 69) | JoinMark = "@@"
type JoinType (line 72) | type JoinType
constant Left (line 75) | Left JoinType = "left"
constant Right (line 76) | Right JoinType = "right"
constant Inner (line 77) | Inner JoinType = "inner"
function NewAlertRuleWorker (line 80) | func NewAlertRuleWorker(rule *models.AlertRule, datasourceId int64, Proc...
function getPromEvalInterval (line 121) | func getPromEvalInterval(schedule cron.Schedule) int {
type sample (line 378) | type sample struct
function getSamples (line 493) | func getSamples(value model.Value) []sample {
function removeVal (line 529) | func removeVal(promql string) string {
function getParamKeyAllLabel (line 633) | func getParamKeyAllLabel(paramKey string, promql string, client promsdk....
function mapPermutation (line 732) | func mapPermutation(paramKeys []string, paraMap map[string][]string) [][...
function combine (line 740) | func combine(paramKeys []string, paraMap map[string][]string, index int,...
function flatten (line 942) | func flatten(rehashed map[uint64][][]uint64) map[uint64][]uint64 {
function onJoin (line 964) | func onJoin(reHashTagIndex1 map[uint64][][]uint64, reHashTagIndex2 map[u...
function rehashSet (line 999) | func rehashSet(seriesTagIndex1 map[uint64][]uint64, seriesStore map[uint...
function cartesianJoin (line 1020) | func cartesianJoin(seriesTagIndex1 map[uint64][]uint64, seriesTagIndex2 ...
function noneJoin (line 1033) | func noneJoin(seriesTagIndex1 map[uint64][]uint64, seriesTagIndex2 map[u...
function originalJoin (line 1048) | func originalJoin(seriesTagIndex1 map[uint64][]uint64, seriesTagIndex2 m...
function exclude (line 1070) | func exclude(reHashTagIndex1 map[uint64][][]uint64, reHashTagIndex2 map[...
function MakeSeriesMap (line 1080) | func MakeSeriesMap(series []models.DataResp, seriesTagIndex map[uint64][...
function mergeNewArray (line 1094) | func mergeNewArray(arg ...[]uint64) []uint64 {
function ProcessJoins (line 1102) | func ProcessJoins(ruleId int64, trigger models.Trigger, seriesTagIndexes...
function GetQueryRef (line 1165) | func GetQueryRef(query interface{}) (string, error) {
function getString (line 1200) | func getString(query interface{}) string {
function GetQueryRefAndUnit (line 1211) | func GetQueryRefAndUnit(query interface{}) (string, string, error) {
function hasLabelLossAggregator (line 1348) | func hasLabelLossAggregator(query models.PromQuery) bool {
function notExactMatch (line 1368) | func notExactMatch(query models.PromQuery) bool {
function ExtractVarMapping (line 1379) | func ExtractVarMapping(promql string) map[string]string {
function fillVar (line 1432) | func fillVar(curRealQuery string, paramKey string, val string) string {
function ExecuteQueryTemplate (line 1723) | func ExecuteQueryTemplate(cate string, query interface{}, data interface...
function ExecuteSqlTemplate (line 1754) | func ExecuteSqlTemplate(query string, data interface{}) (string, error) {
FILE: alert/eval/eval_test.go
function Test_originalJoin (line 37) | func Test_originalJoin(t *testing.T) {
function Test_exclude (line 75) | func Test_exclude(t *testing.T) {
function Test_noneJoin (line 117) | func Test_noneJoin(t *testing.T) {
function Test_cartesianJoin (line 150) | func Test_cartesianJoin(t *testing.T) {
function Test_onJoin (line 183) | func Test_onJoin(t *testing.T) {
function allValueDeepEqual (line 252) | func allValueDeepEqual(got, want map[uint64][]uint64) bool {
function allValueDeepEqualOmitOrder (line 274) | func allValueDeepEqualOmitOrder(got, want []string) bool {
function Test_removeVal (line 288) | func Test_removeVal(t *testing.T) {
function TestExtractVarMapping (line 392) | func TestExtractVarMapping(t *testing.T) {
FILE: alert/mute/mute.go
function IsMuted (line 17) | func IsMuted(rule *models.AlertRule, event *models.AlertCurEvent, target...
function TimeSpanMuteStrategy (line 45) | func TimeSpanMuteStrategy(rule *models.AlertRule, event *models.AlertCur...
function IdentNotExistsMuteStrategy (line 116) | func IdentNotExistsMuteStrategy(rule *models.AlertRule, event *models.Al...
function BgNotMatchMuteStrategy (line 132) | func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertC...
function EventMuteStrategy (line 153) | func EventMuteStrategy(event *models.AlertCurEvent, alertMuteCache *mems...
function MatchMute (line 170) | func MatchMute(event *models.AlertCurEvent, mute *models.AlertMute, cloc...
FILE: alert/naming/hashring.go
constant NodeReplicas (line 11) | NodeReplicas = 500
type DatasourceHashRingType (line 13) | type DatasourceHashRingType struct
method GetNode (line 42) | func (chr *DatasourceHashRingType) GetNode(datasourceId string, pk str...
method IsHit (line 53) | func (chr *DatasourceHashRingType) IsHit(datasourceId string, pk strin...
method Set (line 64) | func (chr *DatasourceHashRingType) Set(datasourceId string, r *consist...
method Del (line 70) | func (chr *DatasourceHashRingType) Del(datasourceId string) {
method Clear (line 76) | func (chr *DatasourceHashRingType) Clear(engineName string) {
function NewConsistentHashRing (line 22) | func NewConsistentHashRing(replicas int32, nodes []string) *consistent.C...
function RebuildConsistentHashRing (line 31) | func RebuildConsistentHashRing(datasourceId string, nodes []string) {
FILE: alert/naming/heartbeat.go
type Naming (line 18) | type Naming struct
method Heartbeats (line 38) | func (n *Naming) Heartbeats() error {
method loopDeleteInactiveInstances (line 51) | func (n *Naming) loopDeleteInactiveInstances() {
method DeleteInactiveInstances (line 63) | func (n *Naming) DeleteInactiveInstances() {
method loopHeartbeat (line 70) | func (n *Naming) loopHeartbeat() {
method heartbeat (line 80) | func (n *Naming) heartbeat() error {
method ActiveServers (line 170) | func (n *Naming) ActiveServers(datasourceId int64) ([]string, error) {
method ActiveServersByEngineName (line 184) | func (n *Naming) ActiveServersByEngineName() ([]string, error) {
function NewNaming (line 24) | func NewNaming(ctx *ctx.Context, heartbeat aconf.HeartbeatConfig, alertS...
FILE: alert/naming/leader.go
method IamLeader (line 9) | func (n *Naming) IamLeader() bool {
FILE: alert/pipeline/engine/engine.go
type WorkflowEngine (line 13) | type WorkflowEngine struct
method Execute (line 21) | func (e *WorkflowEngine) Execute(pipeline *models.EventPipeline, event...
method initWorkflowContext (line 62) | func (e *WorkflowEngine) initWorkflowContext(pipeline *models.EventPip...
method executeDAG (line 95) | func (e *WorkflowEngine) executeDAG(nodeMap map[string]*models.Workflo...
method executeNode (line 217) | func (e *WorkflowEngine) executeNode(node *models.WorkflowNode, wfCtx ...
method shouldFollowBranch (line 329) | func (e *WorkflowEngine) shouldFollowBranch(nodeID string, outputIndex...
method saveExecutionRecord (line 345) | func (e *WorkflowEngine) saveExecutionRecord(pipeline *models.EventPip...
function NewWorkflowEngine (line 17) | func NewWorkflowEngine(c *ctx.Context) *WorkflowEngine {
FILE: alert/pipeline/pipeline.go
function Init (line 12) | func Init() {
FILE: alert/pipeline/processor/aisummary/ai_summary.go
constant HTTP_STATUS_SUCCESS_MAX (line 24) | HTTP_STATUS_SUCCESS_MAX = 299
type AISummaryConfig (line 28) | type AISummaryConfig struct
method Init (line 53) | func (c *AISummaryConfig) Init(settings interface{}) (models.Processor...
method Process (line 58) | func (c *AISummaryConfig) Process(ctx *ctx.Context, wfCtx *models.Work...
method initHTTPClient (line 94) | func (c *AISummaryConfig) initHTTPClient() error {
method prepareEventInfo (line 114) | func (c *AISummaryConfig) prepareEventInfo(wfCtx *models.WorkflowConte...
method generateAISummary (line 135) | func (c *AISummaryConfig) generateAISummary(eventInfo string) (string,...
type Message (line 36) | type Message struct
type ChatCompletionResponse (line 41) | type ChatCompletionResponse struct
function init (line 49) | func init() {
function convertCustomParam (line 208) | func convertCustomParam(value interface{}) (interface{}, error) {
FILE: alert/pipeline/processor/aisummary/ai_summary_test.go
function TestAISummaryConfig_Process (line 12) | func TestAISummaryConfig_Process(t *testing.T) {
function TestConvertCustomParam (line 77) | func TestConvertCustomParam(t *testing.T) {
FILE: alert/pipeline/processor/callback/callback.go
type HTTPConfig (line 20) | type HTTPConfig struct
type CallbackConfig (line 34) | type CallbackConfig struct
method Init (line 42) | func (c *CallbackConfig) Init(settings interface{}) (models.Processor,...
method Process (line 47) | func (c *CallbackConfig) Process(ctx *ctx.Context, wfCtx *models.Workf...
function init (line 38) | func init() {
FILE: alert/pipeline/processor/common/common.go
function InitProcessor (line 10) | func InitProcessor[T any](settings interface{}) (T, error) {
FILE: alert/pipeline/processor/eventdrop/event_drop.go
type EventDropConfig (line 16) | type EventDropConfig struct
method Init (line 24) | func (c *EventDropConfig) Init(settings interface{}) (models.Processor...
method Process (line 29) | func (c *EventDropConfig) Process(ctx *ctx.Context, wfCtx *models.Work...
function init (line 20) | func init() {
FILE: alert/pipeline/processor/eventupdate/event_update.go
type EventUpdateConfig (line 21) | type EventUpdateConfig struct
method Init (line 29) | func (c *EventUpdateConfig) Init(settings interface{}) (models.Process...
method Process (line 34) | func (c *EventUpdateConfig) Process(ctx *ctx.Context, wfCtx *models.Wo...
function init (line 25) | func init() {
FILE: alert/pipeline/processor/logic/if.go
constant ConditionModeExpression (line 18) | ConditionModeExpression = "expression"
constant ConditionModeTags (line 19) | ConditionModeTags = "tags"
type IfConfig (line 23) | type IfConfig struct
method Init (line 45) | func (c *IfConfig) Init(settings interface{}) (models.Processor, error) {
method Process (line 87) | func (c *IfConfig) Process(ctx *ctx.Context, wfCtx *models.WorkflowCon...
method ProcessWithBranch (line 100) | func (c *IfConfig) ProcessWithBranch(ctx *ctx.Context, wfCtx *models.W...
method evaluateCondition (line 126) | func (c *IfConfig) evaluateCondition(wfCtx *models.WorkflowContext) (b...
method evaluateExpressionCondition (line 141) | func (c *IfConfig) evaluateExpressionCondition(wfCtx *models.WorkflowC...
method evaluateTagsCondition (line 171) | func (c *IfConfig) evaluateTagsCondition(event *models.AlertCurEvent) ...
function init (line 41) | func init() {
FILE: alert/pipeline/processor/logic/switch.go
type SwitchCase (line 17) | type SwitchCase struct
type SwitchConfig (line 38) | type SwitchConfig struct
method Init (line 50) | func (c *SwitchConfig) Init(settings interface{}) (models.Processor, e...
method Process (line 93) | func (c *SwitchConfig) Process(ctx *ctx.Context, wfCtx *models.Workflo...
method ProcessWithBranch (line 111) | func (c *SwitchConfig) ProcessWithBranch(ctx *ctx.Context, wfCtx *mode...
method evaluateCases (line 140) | func (c *SwitchConfig) evaluateCases(wfCtx *models.WorkflowContext) (i...
method evaluateCaseCondition (line 154) | func (c *SwitchConfig) evaluateCaseCondition(caseItem *SwitchCase, wfC...
method evaluateExpressionCondition (line 169) | func (c *SwitchConfig) evaluateExpressionCondition(condition string, w...
method evaluateTagsCondition (line 198) | func (c *SwitchConfig) evaluateTagsCondition(caseItem *SwitchCase, eve...
function init (line 46) | func init() {
FILE: alert/pipeline/processor/relabel/relabel.go
constant REPLACE_DOT (line 19) | REPLACE_DOT = "___"
type RelabelConfig (line 23) | type RelabelConfig struct
method Init (line 40) | func (r *RelabelConfig) Init(settings interface{}) (models.Processor, ...
method Process (line 45) | func (r *RelabelConfig) Process(ctx *ctx.Context, wfCtx *models.Workfl...
function init (line 36) | func init() {
function EventRelabel (line 70) | func EventRelabel(event *models.AlertCurEvent, relabelConfigs []*pconf.R...
FILE: alert/pipeline/processor/utils/utils.go
function TplRender (line 13) | func TplRender(wfCtx *models.WorkflowContext, content string) (string, e...
FILE: alert/process/alert_cur_event.go
type AlertCurEventMap (line 9) | type AlertCurEventMap struct
method SetAll (line 25) | func (a *AlertCurEventMap) SetAll(data map[string]*models.AlertCurEven...
method Set (line 31) | func (a *AlertCurEventMap) Set(key string, value *models.AlertCurEvent) {
method Get (line 37) | func (a *AlertCurEventMap) Get(key string) (*models.AlertCurEvent, boo...
method UpdateLastEvalTime (line 44) | func (a *AlertCurEventMap) UpdateLastEvalTime(key string, lastEvalTime...
method Delete (line 54) | func (a *AlertCurEventMap) Delete(key string) {
method Keys (line 60) | func (a *AlertCurEventMap) Keys() []string {
method GetAll (line 70) | func (a *AlertCurEventMap) GetAll() map[string]*models.AlertCurEvent {
function NewAlertCurEventMap (line 14) | func NewAlertCurEventMap(data map[string]*models.AlertCurEvent) *AlertCu...
FILE: alert/process/process.go
type ExternalProcessorsType (line 29) | type ExternalProcessorsType struct
method GetExternalAlertRule (line 42) | func (e *ExternalProcessorsType) GetExternalAlertRule(datasourceId, id...
function NewExternalProcessors (line 36) | func NewExternalProcessors() *ExternalProcessorsType {
type HandleEventFunc (line 49) | type HandleEventFunc
type Processor (line 51) | type Processor struct
method Key (line 82) | func (p *Processor) Key() string {
method DatasourceId (line 86) | func (p *Processor) DatasourceId() int64 {
method Hash (line 90) | func (p *Processor) Hash() string {
method Handle (line 127) | func (p *Processor) Handle(anomalyPoints []models.AnomalyPoint, from s...
method BuildEvent (line 200) | func (p *Processor) BuildEvent(anomalyPoint models.AnomalyPoint, from ...
method HandleRecover (line 298) | func (p *Processor) HandleRecover(alertingKeys map[string]struct{}, no...
method HandleRecoverEvent (line 318) | func (p *Processor) HandleRecoverEvent(hashArr []string, now int64, in...
method RecoverSingle (line 358) | func (p *Processor) RecoverSingle(byRecover bool, hash string, now int...
method handleEvent (line 413) | func (p *Processor) handleEvent(events []*models.AlertCurEvent) {
method inhibitEvent (line 460) | func (p *Processor) inhibitEvent(events []*models.AlertCurEvent, highS...
method fireEvent (line 470) | func (p *Processor) fireEvent(event *models.AlertCurEvent) {
method pushEventToQueue (line 522) | func (p *Processor) pushEventToQueue(e *models.AlertCurEvent) {
method RecoverAlertCurEventFromDb (line 535) | func (p *Processor) RecoverAlertCurEventFromDb() {
method fillTags (line 582) | func (p *Processor) fillTags(anomalyPoint models.AnomalyPoint) {
method mayHandleIdent (line 628) | func (p *Processor) mayHandleIdent(event *models.AlertCurEvent) {
method mayHandleGroup (line 644) | func (p *Processor) mayHandleGroup() {
method DeleteProcessEvent (line 652) | func (p *Processor) DeleteProcessEvent(hash string) {
function NewProcessor (line 99) | func NewProcessor(engineName string, rule *models.AlertRule, datasourceI...
function Relabel (line 282) | func Relabel(rule *models.AlertRule, event *models.AlertCurEvent) {
function labelMapToArr (line 658) | func labelMapToArr(m map[string]string) []string {
function Hash (line 672) | func Hash(ruleId, datasourceId int64, vector models.AnomalyPoint) string {
function TagHash (line 676) | func TagHash(vector models.AnomalyPoint) string {
FILE: alert/queue/queue.go
function ReportQueueSize (line 12) | func ReportQueueSize(stats *astats.Stats) {
FILE: alert/record/prom_rule.go
type RecordRuleContext (line 19) | type RecordRuleContext struct
method Key (line 54) | func (rrc *RecordRuleContext) Key() string {
method Hash (line 58) | func (rrc *RecordRuleContext) Hash() string {
method Prepare (line 69) | func (rrc *RecordRuleContext) Prepare() {}
method Start (line 71) | func (rrc *RecordRuleContext) Start() {
method Eval (line 76) | func (rrc *RecordRuleContext) Eval() {
method Stop (line 113) | func (rrc *RecordRuleContext) Stop() {
function NewRecordRuleContext (line 29) | func NewRecordRuleContext(rule *models.RecordingRule, datasourceId int64...
FILE: alert/record/sample.go
constant LabelName (line 15) | LabelName = "__name__"
function ConvertToTimeSeries (line 18) | func ConvertToTimeSeries(value model.Value, rule *models.RecordingRule) ...
function labelsToLabelsProto (line 92) | func labelsToLabelsProto(labels model.Metric, rule *models.RecordingRule...
FILE: alert/record/scheduler.go
type Scheduler (line 17) | type Scheduler struct
method LoopSyncRules (line 52) | func (s *Scheduler) LoopSyncRules(ctx context.Context) {
method syncRecordRules (line 65) | func (s *Scheduler) syncRecordRules() {
function NewScheduler (line 33) | func NewScheduler(aconf aconf.Alert, rrc *memsto.RecordingRuleCacheType,...
FILE: alert/router/router.go
type Router (line 16) | type Router struct
method Config (line 43) | func (rt *Router) Config(r *gin.Engine) {
function New (line 28) | func New(httpConfig httpx.Config, alert aconf.Alert, amc *memsto.AlertMu...
function Render (line 60) | func Render(c *gin.Context, data, msg interface{}) {
function Dangerous (line 71) | func Dangerous(c *gin.Context, v interface{}, code ...int) {
FILE: alert/router/router_alert_eval_detail.go
method alertEvalDetail (line 12) | func (rt *Router) alertEvalDetail(c *gin.Context) {
FILE: alert/router/router_event.go
method pushEventToQueue (line 22) | func (rt *Router) pushEventToQueue(c *gin.Context) {
method eventPersist (line 85) | func (rt *Router) eventPersist(c *gin.Context) {
type eventForm (line 93) | type eventForm struct
method makeEvent (line 101) | func (rt *Router) makeEvent(c *gin.Context) {
function forwardEvent (line 140) | func forwardEvent(event *eventForm, instance string) error {
FILE: alert/router/router_event_detail.go
method eventDetail (line 12) | func (rt *Router) eventDetail(c *gin.Context) {
FILE: alert/router/router_trace_logs.go
method traceLogs (line 12) | func (rt *Router) traceLogs(c *gin.Context) {
FILE: alert/sender/callback.go
type CallBacker (line 21) | type CallBacker interface
type CallBackContext (line 26) | type CallBackContext struct
type DefaultCallBacker (line 36) | type DefaultCallBacker struct
method CallBack (line 111) | func (c *DefaultCallBacker) CallBack(ctx CallBackContext) {
function BuildCallBackContext (line 39) | func BuildCallBackContext(ctx *ctx.Context, callBackURL string, rule *mo...
function ExtractAtsParams (line 55) | func ExtractAtsParams(rawURL string) []string {
function NewCallBacker (line 73) | func NewCallBacker(
function doSendAndRecord (line 136) | func doSendAndRecord(ctx *ctx.Context, url, token string, body interface...
function NotifyRecord (line 142) | func NotifyRecord(ctx *ctx.Context, evts []*models.AlertCurEvent, notify...
function doSend (line 167) | func doSend(url string, body interface{}, channel string, stats *astats....
type TaskCreateReply (line 183) | type TaskCreateReply struct
function PushCallbackEvent (line 188) | func PushCallbackEvent(ctx *ctx.Context, webhook *models.Webhook, event ...
FILE: alert/sender/dingtalk.go
type dingtalkMarkdown (line 10) | type dingtalkMarkdown struct
type dingtalkAt (line 15) | type dingtalkAt struct
type dingtalk (line 20) | type dingtalk struct
type DingtalkSender (line 30) | type DingtalkSender struct
method Send (line 34) | func (ds *DingtalkSender) Send(ctx MessageContext) {
method CallBack (line 74) | func (ds *DingtalkSender) CallBack(ctx CallBackContext) {
method extract (line 104) | func (ds *DingtalkSender) extract(users []*models.User) ([]string, []s...
FILE: alert/sender/email.go
type EmailSender (line 21) | type EmailSender struct
method Send (line 32) | func (es *EmailSender) Send(ctx MessageContext) {
method WriteEmail (line 82) | func (es *EmailSender) WriteEmail(subject, content string, tos []strin...
type EmailContext (line 27) | type EmailContext struct
function extract (line 50) | func extract(users []*models.User) []string {
function SendEmail (line 60) | func SendEmail(subject, content string, tos []string, stmp aconf.SMTPCon...
function dialSmtp (line 93) | func dialSmtp(d *gomail.Dialer) gomail.SendCloser {
function RestartEmailSender (line 113) | func RestartEmailSender(ctx *ctx.Context, smtp aconf.SMTPConfig) {
function InitEmailSender (line 121) | func InitEmailSender(ctx *ctx.Context, ncc *memsto.NotifyConfigCacheType) {
function updateSmtp (line 128) | func updateSmtp(ctx *ctx.Context, ncc *memsto.NotifyConfigCacheType) {
function startEmailSender (line 141) | func startEmailSender(ctx *ctx.Context, smtp aconf.SMTPConfig) {
FILE: alert/sender/feishu.go
type feishuContent (line 11) | type feishuContent struct
type feishuAt (line 15) | type feishuAt struct
type feishu (line 20) | type feishu struct
type FeishuSender (line 30) | type FeishuSender struct
method CallBack (line 34) | func (fs *FeishuSender) CallBack(ctx CallBackContext) {
method Send (line 60) | func (fs *FeishuSender) Send(ctx MessageContext) {
method extract (line 83) | func (fs *FeishuSender) extract(users []*models.User) ([]string, []str...
FILE: alert/sender/feishucard.go
type Conf (line 12) | type Conf struct
type Te (line 17) | type Te struct
type Element (line 22) | type Element struct
type Titles (line 29) | type Titles struct
type Headers (line 34) | type Headers struct
type Cards (line 39) | type Cards struct
type feishuCard (line 45) | type feishuCard struct
type FeishuCardSender (line 50) | type FeishuCardSender struct
method CallBack (line 95) | func (fs *FeishuCardSender) CallBack(ctx CallBackContext) {
method Send (line 141) | func (fs *FeishuCardSender) Send(ctx MessageContext) {
method extract (line 166) | func (fs *FeishuCardSender) extract(users []*models.User) ([]string, [...
constant Recovered (line 55) | Recovered = "recovered"
constant Triggered (line 56) | Triggered = "triggered"
function createFeishuCardBody (line 59) | func createFeishuCardBody() feishuCard {
FILE: alert/sender/global_webhook.go
constant staticGlobalWebhookChannel (line 24) | staticGlobalWebhookChannel = "static_global_webhook"
function InitStaticGlobalWebhook (line 26) | func InitStaticGlobalWebhook(conf aconf.GlobalWebhook) {
function SendStaticGlobalWebhook (line 60) | func SendStaticGlobalWebhook(ctx *ctx.Context, event *models.AlertCurEve...
FILE: alert/sender/global_webhook_test.go
type roundTripperFunc (line 17) | type roundTripperFunc
method RoundTrip (line 19) | func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response...
function newStaticWebhookTestStats (line 23) | func newStaticWebhookTestStats() *astats.Stats {
function TestSendStaticGlobalWebhookRecordsNewRequestFailure (line 36) | func TestSendStaticGlobalWebhookRecordsNewRequestFailure(t *testing.T) {
function TestSendStaticGlobalWebhookRecordsTransportFailure (line 74) | func TestSendStaticGlobalWebhookRecordsTransportFailure(t *testing.T) {
FILE: alert/sender/ibex.go
type IbexCallBacker (line 25) | type IbexCallBacker struct
method CallBack (line 31) | func (c *IbexCallBacker) CallBack(ctx CallBackContext) {
method handleIbex (line 47) | func (c *IbexCallBacker) handleIbex(ctx *ctx.Context, url string, even...
function CallIbex (line 92) | func CallIbex(ctx *ctx.Context, id int64, host string,
function CanDoIbex (line 197) | func CanDoIbex(username string, tpl *models.TaskTpl, host string, target...
function TaskAdd (line 211) | func TaskAdd(f models.TaskForm, authUser string, isCenter bool) (int64, ...
function cleanHosts (line 282) | func cleanHosts(formHosts []string) []string {
FILE: alert/sender/lark.go
type LarkSender (line 14) | type LarkSender struct
method CallBack (line 18) | func (lk *LarkSender) CallBack(ctx CallBackContext) {
method Send (line 33) | func (lk *LarkSender) Send(ctx MessageContext) {
method extract (line 50) | func (lk *LarkSender) extract(users []*models.User) ([]string, []strin...
FILE: alert/sender/larkcard.go
type LarkCardSender (line 12) | type LarkCardSender struct
method CallBack (line 16) | func (fs *LarkCardSender) CallBack(ctx CallBackContext) {
method Send (line 62) | func (fs *LarkCardSender) Send(ctx MessageContext) {
method extract (line 87) | func (fs *LarkCardSender) extract(users []*models.User) ([]string, []s...
FILE: alert/sender/mm.go
type MatterMostMessage (line 15) | type MatterMostMessage struct
type mm (line 21) | type mm struct
type MmSender (line 27) | type MmSender struct
method Send (line 31) | func (ms *MmSender) Send(ctx MessageContext) {
method CallBack (line 49) | func (ms *MmSender) CallBack(ctx CallBackContext) {
method extract (line 62) | func (ms *MmSender) extract(users []*models.User) []string {
function SendMM (line 72) | func SendMM(ctx *ctx.Context, message MatterMostMessage, events []*model...
function MapStrToStr (line 111) | func MapStrToStr(arr []string, fn func(s string) string) []string {
FILE: alert/sender/notify_record_queue.go
function ReportNotifyRecordQueueSize (line 18) | func ReportNotifyRecordQueueSize(stats *astats.Stats) {
function PushNotifyRecords (line 27) | func PushNotifyRecords(records []*models.NotificationRecord) error {
type NotifyRecordConsumer (line 38) | type NotifyRecordConsumer struct
method LoopConsume (line 49) | func (c *NotifyRecordConsumer) LoopConsume() {
method consume (line 71) | func (c *NotifyRecordConsumer) consume(notis []*models.NotificationRec...
function NewNotifyRecordConsumer (line 42) | func NewNotifyRecordConsumer(ctx *ctx.Context) *NotifyRecordConsumer {
FILE: alert/sender/plugin.go
function MayPluginNotify (line 20) | func MayPluginNotify(ctx *ctx.Context, noticeBytes []byte, notifyScript ...
function alertingCallScript (line 28) | func alertingCallScript(ctx *ctx.Context, stdinBytes []byte, notifyScrip...
function buildErr (line 132) | func buildErr(err error, isTimeout bool) error {
FILE: alert/sender/plugin_cmd_unix.go
function startCmd (line 11) | func startCmd(c *exec.Cmd) error {
FILE: alert/sender/plugin_cmd_windows.go
function startCmd (line 5) | func startCmd(c *exec.Cmd) error {
FILE: alert/sender/sender.go
type Sender (line 16) | type Sender interface
type MessageContext (line 21) | type MessageContext struct
function NewSender (line 30) | func NewSender(key string, tpls map[string]*template.Template, smtp ...a...
function BuildMessageContext (line 54) | func BuildMessageContext(ctx *ctx.Context, rule *models.AlertRule, event...
type BuildTplMessageFunc (line 66) | type BuildTplMessageFunc
function buildTplMessage (line 70) | func buildTplMessage(channel string, tpl *template.Template, events []*m...
FILE: alert/sender/telegram.go
type TelegramMessage (line 15) | type TelegramMessage struct
type telegram (line 21) | type telegram struct
type TelegramSender (line 30) | type TelegramSender struct
method CallBack (line 34) | func (ts *TelegramSender) CallBack(ctx CallBackContext) {
method Send (line 47) | func (ts *TelegramSender) Send(ctx MessageContext) {
method extract (line 61) | func (ts *TelegramSender) extract(users []*models.User) []string {
function SendTelegram (line 71) | func SendTelegram(ctx *ctx.Context, message TelegramMessage, events []*m...
FILE: alert/sender/webhook.go
function getWebhookClient (line 25) | func getWebhookClient(webhook *models.Webhook) *http.Client {
function sendWebhook (line 63) | func sendWebhook(webhook *models.Webhook, event interface{}, stats *asta...
function SingleSendWebhooks (line 129) | func SingleSendWebhooks(ctx *ctx.Context, webhooks map[string]*models.We...
function BatchSendWebhooks (line 146) | func BatchSendWebhooks(ctx *ctx.Context, webhooks map[string]*models.Web...
constant QueueMaxSize (line 158) | QueueMaxSize = 100000
type WebhookQueue (line 160) | type WebhookQueue struct
function PushEvent (line 165) | func PushEvent(ctx *ctx.Context, webhook *models.Webhook, event *models....
function StartConsumer (line 190) | func StartConsumer(ctx *ctx.Context, queue *WebhookQueue, popSize int, w...
FILE: alert/sender/webhook_event_queue.go
type SafeEventQueue (line 10) | type SafeEventQueue struct
method Len (line 34) | func (spq *SafeEventQueue) Len() int {
method len (line 41) | func (spq *SafeEventQueue) len() int {
method Push (line 45) | func (spq *SafeEventQueue) Push(event *models.AlertCurEvent) bool {
method pop (line 68) | func (spq *SafeEventQueue) pop() *models.AlertCurEvent {
method Pop (line 89) | func (spq *SafeEventQueue) Pop() *models.AlertCurEvent {
method PopN (line 95) | func (spq *SafeEventQueue) PopN(n int) []*models.AlertCurEvent {
constant High (line 19) | High = 1
constant Middle (line 20) | Middle = 2
constant Low (line 21) | Low = 3
function NewSafeEventQueue (line 24) | func NewSafeEventQueue(maxSize int) *SafeEventQueue {
FILE: alert/sender/webhook_event_queue_test.go
function TestSafePriorityQueue_ConcurrentPushPop (line 12) | func TestSafePriorityQueue_ConcurrentPushPop(t *testing.T) {
function TestSafePriorityQueue_ConcurrentPopMax (line 58) | func TestSafePriorityQueue_ConcurrentPopMax(t *testing.T) {
function TestSafePriorityQueue_ConcurrentPushPopWithDifferentSeverities (line 92) | func TestSafePriorityQueue_ConcurrentPushPopWithDifferentSeverities(t *t...
function TestSafePriorityQueue_ExceedMaxSize (line 130) | func TestSafePriorityQueue_ExceedMaxSize(t *testing.T) {
FILE: alert/sender/webhook_queue.go
type SafeList (line 10) | type SafeList struct
method PushFront (line 19) | func (sl *SafeList) PushFront(v interface{}) *list.Element {
method PushFrontBatch (line 26) | func (sl *SafeList) PushFrontBatch(vs []interface{}) {
method PopBack (line 34) | func (sl *SafeList) PopBack(max int) []*models.AlertCurEvent {
method RemoveAll (line 60) | func (sl *SafeList) RemoveAll() {
method Len (line 66) | func (sl *SafeList) Len() int {
function NewSafeList (line 15) | func NewSafeList() *SafeList {
type SafeListLimited (line 74) | type SafeListLimited struct
method PopBack (line 83) | func (sll *SafeListLimited) PopBack(max int) []*models.AlertCurEvent {
method PushFront (line 87) | func (sll *SafeListLimited) PushFront(v interface{}) bool {
method PushFrontBatch (line 96) | func (sll *SafeListLimited) PushFrontBatch(vs []interface{}) bool {
method RemoveAll (line 105) | func (sll *SafeListLimited) RemoveAll() {
method Len (line 109) | func (sll *SafeListLimited) Len() int {
function NewSafeListLimited (line 79) | func NewSafeListLimited(maxSize int) *SafeListLimited {
FILE: alert/sender/wecom.go
type wecomMarkdown (line 10) | type wecomMarkdown struct
type wecom (line 14) | type wecom struct
type WecomSender (line 23) | type WecomSender struct
method CallBack (line 27) | func (ws *WecomSender) CallBack(ctx CallBackContext) {
method Send (line 43) | func (ws *WecomSender) Send(ctx MessageContext) {
method extract (line 60) | func (ws *WecomSender) extract(users []*models.User) ([]string, []stri...
FILE: center/cconf/conf.go
type Center (line 9) | type Center struct
method PreCheck (line 44) | func (c *Center) PreCheck() {
type Plugin (line 26) | type Plugin struct
type FlashDuty (line 33) | type FlashDuty struct
type AnonymousAccess (line 39) | type AnonymousAccess struct
FILE: center/cconf/event_example.go
constant EVENT_EXAMPLE (line 3) | EVENT_EXAMPLE = `
FILE: center/cconf/metric.go
type MetricDescType (line 10) | type MetricDescType struct
function GetMetricDesc (line 19) | func GetMetricDesc(lang, metric string) string {
function LoadMetricsYaml (line 43) | func LoadMetricsYaml(configDir, metricsYamlFile string) error {
FILE: center/cconf/ops.go
type Operation (line 13) | type Operation struct
type Ops (line 17) | type Ops struct
type SingleOp (line 24) | type SingleOp struct
function TransformNames (line 29) | func TransformNames(name []string, nameToName map[string]string) []string {
function LoadOpsYaml (line 39) | func LoadOpsYaml(configDir string, opsYamlFile string) error {
function GetAllOps (line 58) | func GetAllOps(ops []Ops) []SingleOp {
function MergeOperationConf (line 66) | func MergeOperationConf() error {
constant builtInOps (line 86) | builtInOps = `
FILE: center/cconf/rsa/rsa_conf.go
function InitRSAConfig (line 16) | func InitRSAConfig(ctx *ctx.Context, rsaConfig *httpx.RSAConfig) error {
function initRSAKeyPairs (line 55) | func initRSAKeyPairs(ctx *ctx.Context, rsaPassWord string) (password str...
function readConfigFile (line 94) | func readConfigFile(rsaConfig *httpx.RSAConfig) (privateBuf, publicBuf [...
FILE: center/center.go
function Initialize (line 44) | func Initialize(configDir string, cryptoKey string) (func(), error) {
function InitSiteUrl (line 171) | func InitSiteUrl(ctx *ctx.Context, serverIP string, serverPort int) {
FILE: center/cstats/stats.go
constant namespace (line 10) | namespace = "n9e"
constant subsystem (line 11) | subsystem = "center"
function init (line 46) | func init() {
function recordUptime (line 58) | func recordUptime() {
FILE: center/integration/init.go
constant SYSTEM (line 20) | SYSTEM = "system"
type BuiltinPayloadInFileType (line 24) | type BuiltinPayloadInFileType struct
method AddBuiltinPayload (line 317) | func (b *BuiltinPayloadInFileType) AddBuiltinPayload(bp *models.Builti...
method GetComponentIdentByCate (line 334) | func (b *BuiltinPayloadInFileType) GetComponentIdentByCate(typ, cate s...
method GetBuiltinPayload (line 358) | func (b *BuiltinPayloadInFileType) GetBuiltinPayload(typ, cate, query ...
method GetBuiltinPayloadCates (line 393) | func (b *BuiltinPayloadInFileType) GetBuiltinPayloadCates(typ string, ...
method BuiltinMetricGets (line 427) | func (b *BuiltinPayloadInFileType) BuiltinMetricGets(metricsInDB []*mo...
method BuiltinMetricTypes (line 503) | func (b *BuiltinPayloadInFileType) BuiltinMetricTypes(lang, collector,...
method BuiltinMetricCollectors (line 516) | func (b *BuiltinPayloadInFileType) BuiltinMetricCollectors(lang, typ, ...
function Init (line 31) | func Init(ctx *ctx.Context, builtinIntegrationsDir string) {
type BuiltinBoard (line 289) | type BuiltinBoard struct
function NewBuiltinPayloadInFileType (line 309) | func NewBuiltinPayloadInFileType() *BuiltinPayloadInFileType {
function filterByQuery (line 412) | func filterByQuery(payloads []*models.BuiltinPayload, query string) []*m...
function applyFilter (line 528) | func applyFilter(metric *models.BuiltinMetric, collector, typ, query, un...
function containsUnit (line 548) | func containsUnit(unit, metricUnit string) bool {
function applyQueryFilter (line 558) | func applyQueryFilter(metric *models.BuiltinMetric, query string) bool {
function getTranslationWithLanguage (line 575) | func getTranslationWithLanguage(bm *models.BuiltinMetric, lang string) (...
function convertBuiltinMetricByDB (line 594) | func convertBuiltinMetricByDB(metricsInDB []*models.BuiltinMetric) map[s...
function getDefaultTranslation (line 640) | func getDefaultTranslation(bm *models.BuiltinMetric) []models.Translation {
FILE: center/metas/metas.go
type Set (line 16) | type Set struct
method Init (line 32) | func (s *Set) Init() {
method MSet (line 36) | func (s *Set) MSet(items map[string]models.HostMeta) {
method Set (line 44) | func (s *Set) Set(ident string, meta models.HostMeta) {
method LoopPersist (line 50) | func (s *Set) LoopPersist() {
method persist (line 57) | func (s *Set) persist() {
method updateMeta (line 73) | func (s *Set) updateMeta(items map[string]models.HostMeta) {
method updateTargets (line 94) | func (s *Set) updateTargets(m map[string]models.HostMeta) error {
function New (line 22) | func New(redis storage.Redis) *Set {
FILE: center/router/router.go
type Router (line 36) | type Router struct
method configNoRoute (line 127) | func (rt *Router) configNoRoute(r *gin.Engine, fs *http.FileSystem) {
method Config (line 163) | func (rt *Router) Config(r *gin.Engine) {
function New (line 61) | func New(httpConfig httpx.Config, center cconf.Center, alert aconf.Alert...
function stat (line 91) | func stat() gin.HandlerFunc {
function languageDetector (line 104) | func languageDetector(i18NHeaderKey string) gin.HandlerFunc {
function Render (line 743) | func Render(c *gin.Context, data, msg interface{}) {
function Dangerous (line 754) | func Dangerous(c *gin.Context, v interface{}, code ...int) {
FILE: center/router/router_alert_aggr_view.go
method alertAggrViewGets (line 13) | func (rt *Router) alertAggrViewGets(c *gin.Context) {
method alertAggrViewAdd (line 19) | func (rt *Router) alertAggrViewAdd(c *gin.Context) {
method alertAggrViewDel (line 37) | func (rt *Router) alertAggrViewDel(c *gin.Context) {
method alertAggrViewPut (line 51) | func (rt *Router) alertAggrViewPut(c *gin.Context) {
FILE: center/router/router_alert_cur_event.go
function getUserGroupIds (line 19) | func getUserGroupIds(ctx *gin.Context, rt *Router, myGroups bool) ([]int...
method alertCurEventsCard (line 27) | func (rt *Router) alertCurEventsCard(c *gin.Context) {
type AlertCard (line 123) | type AlertCard struct
method alertCurEventsCardDetails (line 130) | func (rt *Router) alertCurEventsCardDetails(c *gin.Context) {
method alertCurEventsGetByRid (line 146) | func (rt *Router) alertCurEventsGetByRid(c *gin.Context) {
method alertCurEventsList (line 153) | func (rt *Router) alertCurEventsList(c *gin.Context) {
method alertCurEventDel (line 205) | func (rt *Router) alertCurEventDel(c *gin.Context) {
method checkCurEventBusiGroupRWPermission (line 215) | func (rt *Router) checkCurEventBusiGroupRWPermission(c *gin.Context, ids...
method alertCurEventGet (line 234) | func (rt *Router) alertCurEventGet(c *gin.Context) {
function GetCurEventDetail (line 248) | func GetCurEventDetail(ctx *ctx.Context, eid int64) (*models.AlertCurEve...
function GetEventNotifyRuleNames (line 271) | func GetEventNotifyRuleNames(ctx *ctx.Context, notifyRuleIds []int64) ([...
function GetEventNotifyVersion (line 287) | func GetEventNotifyVersion(ctx *ctx.Context, ruleId int64, notifyRuleIds...
method alertCurEventsStatistics (line 300) | func (rt *Router) alertCurEventsStatistics(c *gin.Context) {
method alertCurEventDelByHash (line 305) | func (rt *Router) alertCurEventDelByHash(c *gin.Context) {
method eventTagKeys (line 310) | func (rt *Router) eventTagKeys(c *gin.Context) {
method eventTagValues (line 360) | func (rt *Router) eventTagValues(c *gin.Context) {
FILE: center/router/router_alert_eval_detail.go
method alertEvalDetailPage (line 21) | func (rt *Router) alertEvalDetailPage(c *gin.Context) {
method alertEvalDetailJSON (line 47) | func (rt *Router) alertEvalDetailJSON(c *gin.Context) {
method getAlertEvalLogs (line 63) | func (rt *Router) getAlertEvalLogs(id string) ([]string, string, error) {
method forwardAlertEvalDetail (line 132) | func (rt *Router) forwardAlertEvalDetail(node, id string) ([]string, str...
FILE: center/router/router_alert_his_event.go
function getTimeRange (line 18) | func getTimeRange(c *gin.Context) (stime, etime int64) {
method alertHisEventsList (line 34) | func (rt *Router) alertHisEventsList(c *gin.Context) {
type alertHisEventsDeleteForm (line 83) | type alertHisEventsDeleteForm struct
method alertHisEventsDelete (line 88) | func (rt *Router) alertHisEventsDelete(c *gin.Context) {
function init (line 123) | func init() {
function transferEventToCur (line 127) | func transferEventToCur(ctx *ctx.Context, event *models.AlertHisEvent) *...
method alertHisEventGet (line 132) | func (rt *Router) alertHisEventGet(c *gin.Context) {
function GetBusinessGroupIds (line 159) | func GetBusinessGroupIds(c *gin.Context, ctx *ctx.Context, onlySelfGroup...
FILE: center/router/router_alert_rule.go
type AlertRuleModifyHookFunc (line 28) | type AlertRuleModifyHookFunc
method alertRuleGets (line 31) | func (rt *Router) alertRuleGets(c *gin.Context) {
function GetAlertCueEventTimeRange (line 44) | func GetAlertCueEventTimeRange(c *gin.Context) (stime, etime int64) {
method alertRuleGetsByGids (line 56) | func (rt *Router) alertRuleGetsByGids(c *gin.Context) {
method alertRulesGetByService (line 103) | func (rt *Router) alertRulesGetByService(c *gin.Context) {
method alertRuleAddByFE (line 136) | func (rt *Router) alertRuleAddByFE(c *gin.Context) {
type AlertRuleTryRunForm (line 153) | type AlertRuleTryRunForm struct
method alertRuleNotifyTryRun (line 158) | func (rt *Router) alertRuleNotifyTryRun(c *gin.Context) {
method alertRuleEnableTryRun (line 237) | func (rt *Router) alertRuleEnableTryRun(c *gin.Context) {
method alertRuleAddByImport (line 267) | func (rt *Router) alertRuleAddByImport(c *gin.Context) {
type promRuleForm (line 301) | type promRuleForm struct
method alertRuleAddByImportPromRule (line 307) | func (rt *Router) alertRuleAddByImportPromRule(c *gin.Context) {
method alertRuleAddByService (line 362) | func (rt *Router) alertRuleAddByService(c *gin.Context) {
method alertRuleAddOneByService (line 374) | func (rt *Router) alertRuleAddOneByService(c *gin.Context) {
method alertRuleAddForService (line 385) | func (rt *Router) alertRuleAddForService(lst []models.AlertRule, usernam...
method alertRuleAdd (line 410) | func (rt *Router) alertRuleAdd(lst []models.AlertRule, username string, ...
method alertRuleDel (line 436) | func (rt *Router) alertRuleDel(c *gin.Context) {
method alertRuleDelByService (line 445) | func (rt *Router) alertRuleDelByService(c *gin.Context) {
method alertRulePutByFE (line 452) | func (rt *Router) alertRulePutByFE(c *gin.Context) {
method alertRulePutByService (line 471) | func (rt *Router) alertRulePutByService(c *gin.Context) {
type alertRuleFieldForm (line 486) | type alertRuleFieldForm struct
method alertRulePutFields (line 493) | func (rt *Router) alertRulePutFields(c *gin.Context) {
method alertRuleGet (line 596) | func (rt *Router) alertRuleGet(c *gin.Context) {
method alertRulePureGet (line 618) | func (rt *Router) alertRulePureGet(c *gin.Context) {
method alertRuleValidation (line 633) | func (rt *Router) alertRuleValidation(c *gin.Context) {
method alertRuleCallbacks (line 683) | func (rt *Router) alertRuleCallbacks(c *gin.Context) {
type alertRuleTestForm (line 705) | type alertRuleTestForm struct
method relabelTest (line 710) | func (rt *Router) relabelTest(c *gin.Context) {
type identListForm (line 752) | type identListForm struct
function containsIdentOperator (line 757) | func containsIdentOperator(s string) bool {
method cloneToMachine (line 766) | func (rt *Router) cloneToMachine(c *gin.Context) {
type alertBatchCloneForm (line 840) | type alertBatchCloneForm struct
method batchAlertRuleClone (line 846) | func (rt *Router) batchAlertRuleClone(c *gin.Context) {
method timezonesGet (line 886) | func (rt *Router) timezonesGet(c *gin.Context) {
FILE: center/router/router_alert_subscribe.go
method alertSubscribeGets (line 19) | func (rt *Router) alertSubscribeGets(c *gin.Context) {
method alertSubscribeGetsByGids (line 38) | func (rt *Router) alertSubscribeGetsByGids(c *gin.Context) {
method alertSubscribeGet (line 75) | func (rt *Router) alertSubscribeGet(c *gin.Context) {
method alertSubscribeAdd (line 97) | func (rt *Router) alertSubscribeAdd(c *gin.Context) {
type SubscribeTryRunForm (line 113) | type SubscribeTryRunForm struct
method alertSubscribeTryRun (line 118) | func (rt *Router) alertSubscribeTryRun(c *gin.Context) {
method alertSubscribePut (line 255) | func (rt *Router) alertSubscribePut(c *gin.Context) {
method alertSubscribeDel (line 300) | func (rt *Router) alertSubscribeDel(c *gin.Context) {
method alertSubscribeGetsByService (line 308) | func (rt *Router) alertSubscribeGetsByService(c *gin.Context) {
FILE: center/router/router_board.go
type boardForm (line 16) | type boardForm struct
method boardAdd (line 27) | func (rt *Router) boardAdd(c *gin.Context) {
method boardGet (line 54) | func (rt *Router) boardGet(c *gin.Context) {
method boardGetsByBids (line 105) | func (rt *Router) boardGetsByBids(c *gin.Context) {
method boardPureGet (line 112) | func (rt *Router) boardPureGet(c *gin.Context) {
method boardDel (line 128) | func (rt *Router) boardDel(c *gin.Context) {
method Board (line 155) | func (rt *Router) Board(id int64) *models.Board {
method boardPut (line 167) | func (rt *Router) boardPut(c *gin.Context) {
method boardPutConfigs (line 198) | func (rt *Router) boardPutConfigs(c *gin.Context) {
method boardPutPublic (line 228) | func (rt *Router) boardPutPublic(c *gin.Context) {
method boardGets (line 258) | func (rt *Router) boardGets(c *gin.Context) {
method publicBoardGets (line 269) | func (rt *Router) publicBoardGets(c *gin.Context) {
method boardGetsByGids (line 285) | func (rt *Router) boardGetsByGids(c *gin.Context) {
method boardClone (line 326) | func (rt *Router) boardClone(c *gin.Context) {
type boardsForm (line 345) | type boardsForm struct
method boardBatchClone (line 350) | func (rt *Router) boardBatchClone(c *gin.Context) {
FILE: center/router/router_builtin.go
method builtinCateFavoriteAdd (line 20) | func (rt *Router) builtinCateFavoriteAdd(c *gin.Context) {
method builtinCateFavoriteDel (line 35) | func (rt *Router) builtinCateFavoriteDel(c *gin.Context) {
type Payload (line 42) | type Payload struct
type BoardCate (line 50) | type BoardCate struct
method builtinBoardDetailGets (line 57) | func (rt *Router) builtinBoardDetailGets(c *gin.Context) {
method builtinBoardCateGets (line 74) | func (rt *Router) builtinBoardCateGets(c *gin.Context) {
method builtinBoardGets (line 134) | func (rt *Router) builtinBoardGets(c *gin.Context) {
type AlertCate (line 162) | type AlertCate struct
method builtinAlertCateGets (line 169) | func (rt *Router) builtinAlertCateGets(c *gin.Context) {
type builtinAlertRulesList (line 222) | type builtinAlertRulesList struct
method builtinAlertRules (line 229) | func (rt *Router) builtinAlertRules(c *gin.Context) {
method builtinBoardGet (line 287) | func (rt *Router) builtinBoardGet(c *gin.Context) {
method builtinIcon (line 308) | func (rt *Router) builtinIcon(c *gin.Context) {
method builtinMarkdown (line 319) | func (rt *Router) builtinMarkdown(c *gin.Context) {
FILE: center/router/router_builtin_component.go
constant SYSTEM (line 14) | SYSTEM = "system"
method builtinComponentsAdd (line 16) | func (rt *Router) builtinComponentsAdd(c *gin.Context) {
method builtinComponentsGets (line 37) | func (rt *Router) builtinComponentsGets(c *gin.Context) {
method builtinComponentsPut (line 47) | func (rt *Router) builtinComponentsPut(c *gin.Context) {
method builtinComponentsDel (line 86) | func (rt *Router) builtinComponentsDel(c *gin.Context) {
FILE: center/router/router_builtin_metric_filter.go
method metricFilterGets (line 10) | func (rt *Router) metricFilterGets(c *gin.Context) {
method metricFilterAdd (line 35) | func (rt *Router) metricFilterAdd(c *gin.Context) {
method metricFilterDel (line 46) | func (rt *Router) metricFilterDel(c *gin.Context) {
method metricFilterPut (line 71) | func (rt *Router) metricFilterPut(c *gin.Context) {
type metricPromqlReq (line 93) | type metricPromqlReq struct
method getMetricPromql (line 98) | func (rt *Router) getMetricPromql(c *gin.Context) {
function HasPerm (line 106) | func HasPerm(gids []int64, gps []models.GroupPerm, checkWrite bool) bool {
FILE: center/router/router_builtin_metrics.go
method builtinMetricsAdd (line 17) | func (rt *Router) builtinMetricsAdd(c *gin.Context) {
method builtinMetricsGets (line 42) | func (rt *Router) builtinMetricsGets(c *gin.Context) {
method builtinMetricsPut (line 65) | func (rt *Router) builtinMetricsPut(c *gin.Context) {
method builtinMetricsDel (line 81) | func (rt *Router) builtinMetricsDel(c *gin.Context) {
method builtinMetricsDefaultTypes (line 89) | func (rt *Router) builtinMetricsDefaultTypes(c *gin.Context) {
method builtinMetricsTypes (line 101) | func (rt *Router) builtinMetricsTypes(c *gin.Context) {
method builtinMetricsCollectors (line 128) | func (rt *Router) builtinMetricsCollectors(c *gin.Context) {
FILE: center/router/router_builtin_payload.go
type Board (line 17) | type Board struct
method builtinPayloadsAdd (line 25) | func (rt *Router) builtinPayloadsAdd(c *gin.Context) {
method builtinPayloadsGets (line 197) | func (rt *Router) builtinPayloadsGets(c *gin.Context) {
method builtinPayloadcatesGet (line 221) | func (rt *Router) builtinPayloadcatesGet(c *gin.Context) {
method builtinPayloadsPut (line 253) | func (rt *Router) builtinPayloadsPut(c *gin.Context) {
method builtinPayloadsDel (line 295) | func (rt *Router) builtinPayloadsDel(c *gin.Context) {
method builtinPayloadsGetByUUID (line 304) | func (rt *Router) builtinPayloadsGetByUUID(c *gin.Context) {
FILE: center/router/router_busi_group.go
type busiGroupForm (line 14) | type busiGroupForm struct
method busiGroupAdd (line 21) | func (rt *Router) busiGroupAdd(c *gin.Context) {
method busiGroupPut (line 56) | func (rt *Router) busiGroupPut(c *gin.Context) {
method busiGroupMemberAdd (line 65) | func (rt *Router) busiGroupMemberAdd(c *gin.Context) {
method busiGroupMemberDel (line 81) | func (rt *Router) busiGroupMemberDel(c *gin.Context) {
method busiGroupDel (line 97) | func (rt *Router) busiGroupDel(c *gin.Context) {
method busiGroupGets (line 112) | func (rt *Router) busiGroupGets(c *gin.Context) {
method busiGroupGetsByService (line 129) | func (rt *Router) busiGroupGetsByService(c *gin.Context) {
method busiGroupAlertingsGets (line 135) | func (rt *Router) busiGroupAlertingsGets(c *gin.Context) {
method busiGroupGet (line 141) | func (rt *Router) busiGroupGet(c *gin.Context) {
method busiGroupsGetTags (line 147) | func (rt *Router) busiGroupsGetTags(c *gin.Context) {
FILE: center/router/router_captcha.go
type CaptchaRedisStore (line 14) | type CaptchaRedisStore struct
method Set (line 18) | func (s *CaptchaRedisStore) Set(id string, value string) error {
method Get (line 28) | func (s *CaptchaRedisStore) Get(id string, clear bool) string {
method Verify (line 43) | func (s *CaptchaRedisStore) Verify(id, answer string, clear bool) bool {
method newCaptchaRedisStore (line 49) | func (rt *Router) newCaptchaRedisStore() *CaptchaRedisStore {
type CaptchaReqBody (line 58) | type CaptchaReqBody struct
method generateCaptcha (line 64) | func (rt *Router) generateCaptcha(c *gin.Context) {
method captchaVerify (line 82) | func (rt *Router) captchaVerify(c *gin.Context) {
method ifShowCaptcha (line 96) | func (rt *Router) ifShowCaptcha(c *gin.Context) {
function CaptchaVerify (line 111) | func CaptchaVerify(id string, value string) bool {
FILE: center/router/router_chart_share.go
method chartShareGets (line 13) | func (rt *Router) chartShareGets(c *gin.Context) {
type chartShareForm (line 19) | type chartShareForm struct
method chartShareAdd (line 24) | func (rt *Router) chartShareAdd(c *gin.Context) {
FILE: center/router/router_config.go
method notifyChannelsGets (line 12) | func (rt *Router) notifyChannelsGets(c *gin.Context) {
method contactKeysGets (line 39) | func (rt *Router) contactKeysGets(c *gin.Context) {
method siteInfo (line 66) | func (rt *Router) siteInfo(c *gin.Context) {
FILE: center/router/router_configs.go
constant EMBEDDEDDASHBOARD (line 12) | EMBEDDEDDASHBOARD = "embedded-dashboards"
method configsGet (line 14) | func (rt *Router) configsGet(c *gin.Context) {
method configGet (line 24) | func (rt *Router) configGet(c *gin.Context) {
method configGetAll (line 30) | func (rt *Router) configGetAll(c *gin.Context) {
method configGetByKey (line 35) | func (rt *Router) configGetByKey(c *gin.Context) {
method configPutByKey (line 40) | func (rt *Router) configPutByKey(c *gin.Context) {
method embeddedDashboardsGet (line 47) | func (rt *Router) embeddedDashboardsGet(c *gin.Context) {
method embeddedDashboardsPut (line 52) | func (rt *Router) embeddedDashboardsPut(c *gin.Context) {
method configsDel (line 59) | func (rt *Router) configsDel(c *gin.Context) {
method configsPut (line 65) | func (rt *Router) configsPut(c *gin.Context) { //for APIForService
method configsPost (line 82) | func (rt *Router) configsPost(c *gin.Context) { //for APIForService
FILE: center/router/router_crypto.go
type confPropCrypto (line 10) | type confPropCrypto struct
method confPropEncrypt (line 15) | func (rt *Router) confPropEncrypt(c *gin.Context) {
method confPropDecrypt (line 40) | func (rt *Router) confPropDecrypt(c *gin.Context) {
FILE: center/router/router_dash_annotation.go
function checkAnnotationPermission (line 15) | func checkAnnotationPermission(c *gin.Context, ctx *ctx.Context, dashboa...
method dashAnnotationAdd (line 35) | func (rt *Router) dashAnnotationAdd(c *gin.Context) {
method dashAnnotationGets (line 52) | func (rt *Router) dashAnnotationGets(c *gin.Context) {
method dashAnnotationPut (line 62) | func (rt *Router) dashAnnotationPut(c *gin.Context) {
method dashAnnotationDel (line 79) | func (rt *Router) dashAnnotationDel(c *gin.Context) {
function getAnnotationById (line 90) | func getAnnotationById(ctx *ctx.Context, id int64) (*models.DashAnnotati...
FILE: center/router/router_dashboard.go
type ChartPure (line 3) | type ChartPure struct
type ChartGroupPure (line 8) | type ChartGroupPure struct
type DashboardPure (line 14) | type DashboardPure struct
FILE: center/router/router_datasource.go
method pluginList (line 24) | func (rt *Router) pluginList(c *gin.Context) {
type listReq (line 28) | type listReq struct
method datasourceList (line 34) | func (rt *Router) datasourceList(c *gin.Context) {
method datasourceGetsByService (line 53) | func (rt *Router) datasourceGetsByService(c *gin.Context) {
method datasourceRsaConfigGet (line 67) | func (rt *Router) datasourceRsaConfigGet(c *gin.Context) {
method datasourceBriefs (line 91) | func (rt *Router) datasourceBriefs(c *gin.Context) {
method datasourceUpsert (line 125) | func (rt *Router) datasourceUpsert(c *gin.Context) {
function DatasourceCheck (line 285) | func DatasourceCheck(c *gin.Context, ds models.Datasource) error {
method datasourceGet (line 384) | func (rt *Router) datasourceGet(c *gin.Context) {
method datasourceUpdataStatus (line 396) | func (rt *Router) datasourceUpdataStatus(c *gin.Context) {
method datasourceDel (line 410) | func (rt *Router) datasourceDel(c *gin.Context) {
method getDatasourceIds (line 422) | func (rt *Router) getDatasourceIds(c *gin.Context) {
type datasourceQueryForm (line 429) | type datasourceQueryForm struct
type datasourceQueryResp (line 434) | type datasourceQueryResp struct
method datasourceQuery (line 439) | func (rt *Router) datasourceQuery(c *gin.Context) {
function getElasticsearchVersion (line 465) | func getElasticsearchVersion(ds models.Datasource, timeout time.Duration...
FILE: center/router/router_datasource_db.go
method ShowDatabases (line 15) | func (rt *Router) ShowDatabases(c *gin.Context) {
method ShowTables (line 45) | func (rt *Router) ShowTables(c *gin.Context) {
method DescribeTable (line 75) | func (rt *Router) DescribeTable(c *gin.Context) {
FILE: center/router/router_embedded.go
method embeddedProductGets (line 13) | func (rt *Router) embeddedProductGets(c *gin.Context) {
method embeddedProductGet (line 50) | func (rt *Router) embeddedProductGet(c *gin.Context) {
method embeddedProductAdd (line 70) | func (rt *Router) embeddedProductAdd(c *gin.Context) {
method embeddedProductPut (line 85) | func (rt *Router) embeddedProductPut(c *gin.Context) {
method embeddedProductDelete (line 110) | func (rt *Router) embeddedProductDelete(c *gin.Context) {
function hasEmbeddedProductAccess (line 120) | func hasEmbeddedProductAccess(ctx *ctx.Context, user *models.User, ep *m...
FILE: center/router/router_es.go
type IndexReq (line 12) | type IndexReq struct
type FieldValueReq (line 18) | type FieldValueReq struct
type FieldObj (line 25) | type FieldObj struct
method QueryIndices (line 31) | func (rt *Router) QueryIndices(c *gin.Context) {
method QueryFields (line 47) | func (rt *Router) QueryFields(c *gin.Context) {
method QueryESVariable (line 63) | func (rt *Router) QueryESVariable(c *gin.Context) {
FILE: center/router/router_es_index_pattern.go
method esIndexPatternAdd (line 13) | func (rt *Router) esIndexPatternAdd(c *gin.Context) {
method esIndexPatternPut (line 29) | func (rt *Router) esIndexPatternPut(c *gin.Context) {
method esIndexPatternDel (line 49) | func (rt *Router) esIndexPatternDel(c *gin.Context) {
method esIndexPatternGetList (line 61) | func (rt *Router) esIndexPatternGetList(c *gin.Context) {
method esIndexPatternGet (line 80) | func (rt *Router) esIndexPatternGet(c *gin.Context) {
FILE: center/router/router_event_detail.go
method eventDetailPage (line 20) | func (rt *Router) eventDetailPage(c *gin.Context) {
method eventDetailJSON (line 46) | func (rt *Router) eventDetailJSON(c *gin.Context) {
method getNodeForDatasource (line 65) | func (rt *Router) getNodeForDatasource(datasourceId int64, pk string) (s...
method getEventLogs (line 89) | func (rt *Router) getEventLogs(hash string) ([]string, string, error) {
method forwardEventDetail (line 113) | func (rt *Router) forwardEventDetail(node, hash string) ([]string, strin...
FILE: center/router/router_event_pipeline.go
method eventPipelinesList (line 20) | func (rt *Router) eventPipelinesList(c *gin.Context) {
method getEventPipeline (line 79) | func (rt *Router) getEventPipeline(c *gin.Context) {
method addEventPipeline (line 102) | func (rt *Router) addEventPipeline(c *gin.Context) {
method updateEventPipeline (line 124) | func (rt *Router) updateEventPipeline(c *gin.Context) {
method deleteEventPipelines (line 142) | func (rt *Router) deleteEventPipelines(c *gin.Context) {
method tryRunEventPipeline (line 164) | func (rt *Router) tryRunEventPipeline(c *gin.Context) {
method tryRunEventProcessor (line 211) | func (rt *Router) tryRunEventProcessor(c *gin.Context) {
method tryRunEventProcessorByNotifyRule (line 244) | func (rt *Router) tryRunEventProcessorByNotifyRule(c *gin.Context) {
method eventPipelinesListByService (line 298) | func (rt *Router) eventPipelinesListByService(c *gin.Context) {
type EventPipelineRequest (line 303) | type EventPipelineRequest struct
method executePipelineTrigger (line 313) | func (rt *Router) executePipelineTrigger(pipeline *models.EventPipeline,...
method triggerEventPipelineByService (line 350) | func (rt *Router) triggerEventPipelineByService(c *gin.Context) {
method triggerEventPipelineByAPI (line 373) | func (rt *Router) triggerEventPipelineByAPI(c *gin.Context) {
method listAllEventPipelineExecutions (line 399) | func (rt *Router) listAllEventPipelineExecutions(c *gin.Context) {
method listEventPipelineExecutions (line 423) | func (rt *Router) listEventPipelineExecutions(c *gin.Context) {
method getEventPipelineExecution (line 446) | func (rt *Router) getEventPipelineExecution(c *gin.Context) {
method getEventPipelineExecutionStats (line 457) | func (rt *Router) getEventPipelineExecutionStats(c *gin.Context) {
method cleanEventPipelineExecutions (line 466) | func (rt *Router) cleanEventPipelineExecutions(c *gin.Context) {
method streamEventPipeline (line 485) | func (rt *Router) streamEventPipeline(c *gin.Context) {
method handleStreamResponse (line 530) | func (rt *Router) handleStreamResponse(c *gin.Context, result *models.Wo...
method streamEventPipelineByService (line 583) | func (rt *Router) streamEventPipelineByService(c *gin.Context) {
method eventPipelineExecutionAdd (line 627) | func (rt *Router) eventPipelineExecutionAdd(c *gin.Context) {
FILE: center/router/router_funcs.go
constant defaultLimit (line 15) | defaultLimit = 300
method statistic (line 17) | func (rt *Router) statistic(c *gin.Context) {
function queryDatasourceIds (line 72) | func queryDatasourceIds(c *gin.Context) []int64 {
function queryStrListField (line 84) | func queryStrListField(c *gin.Context, fieldName string, sep ...string) ...
type idsForm (line 101) | type idsForm struct
method Verify (line 106) | func (f idsForm) Verify() {
function User (line 112) | func User(ctx *ctx.Context, id int64) *models.User {
function UserGroup (line 123) | func UserGroup(ctx *ctx.Context, id int64) *models.UserGroup {
function BusiGroup (line 140) | func BusiGroup(ctx *ctx.Context, id int64) *models.BusiGroup {
function Dashboard (line 151) | func Dashboard(ctx *ctx.Context, id int64) *models.Dashboard {
type DoneIdsReply (line 162) | type DoneIdsReply struct
type TaskCreateReply (line 169) | type TaskCreateReply struct
function Username (line 174) | func Username(c *gin.Context) string {
function HasPermission (line 183) | func HasPermission(ctx *ctx.Context, c *gin.Context, sourceType, sourceI...
function ValidateSourceToken (line 199) | func ValidateSourceToken(ctx *ctx.Context, sourceType, sourceId, token s...
FILE: center/router/router_heartbeat.go
type HeartbeatHookFunc (line 24) | type HeartbeatHookFunc
method heartbeat (line 26) | func (rt *Router) heartbeat(c *gin.Context) {
function HandleHeartbeat (line 34) | func HandleHeartbeat(c *gin.Context, ctx *ctx.Context, engineName string...
FILE: center/router/router_login.go
type loginForm (line 30) | type loginForm struct
method loginPost (line 37) | func (rt *Router) loginPost(c *gin.Context) {
method logoutPost (line 104) | func (rt *Router) logoutPost(c *gin.Context) {
type refreshForm (line 144) | type refreshForm struct
method refreshPost (line 148) | func (rt *Router) refreshPost(c *gin.Context) {
method loginRedirect (line 242) | func (rt *Router) loginRedirect(c *gin.Context) {
type CallbackOutput (line 271) | type CallbackOutput struct
method loginCallback (line 278) | func (rt *Router) loginCallback(c *gin.Context) {
type RedirectOutput (line 340) | type RedirectOutput struct
method loginRedirectCas (line 345) | func (rt *Router) loginRedirectCas(c *gin.Context) {
method loginCallbackCas (line 378) | func (rt *Router) loginCallbackCas(c *gin.Context) {
method loginRedirectOAuth (line 426) | func (rt *Router) loginRedirectOAuth(c *gin.Context) {
method loginRedirectDingTalk (line 455) | func (rt *Router) loginRedirectDingTalk(c *gin.Context) {
method loginCallbackDingTalk (line 484) | func (rt *Router) loginCallbackDingTalk(c *gin.Context) {
method loginRedirectFeiShu (line 531) | func (rt *Router) loginRedirectFeiShu(c *gin.Context) {
method loginCallbackFeiShu (line 560) | func (rt *Router) loginCallbackFeiShu(c *gin.Context) {
method loginCallbackOAuth (line 621) | func (rt *Router) loginCallbackOAuth(c *gin.Context) {
type SsoConfigOutput (line 667) | type SsoConfigOutput struct
method ssoConfigNameGet (line 675) | func (rt *Router) ssoConfigNameGet(c *gin.Context) {
method ssoConfigGets (line 706) | func (rt *Router) ssoConfigGets(c *gin.Context) {
method ssoConfigUpdate (line 753) | func (rt *Router) ssoConfigUpdate(c *gin.Context) {
type RSAConfigOutput (line 844) | type RSAConfigOutput struct
method rsaConfigGet (line 849) | func (rt *Router) rsaConfigGet(c *gin.Context) {
FILE: center/router/router_message_template.go
method messageTemplatesAdd (line 21) | func (rt *Router) messageTemplatesAdd(c *gin.Context) {
method messageTemplatesDel (line 66) | func (rt *Router) messageTemplatesDel(c *gin.Context) {
method messageTemplatePut (line 93) | func (rt *Router) messageTemplatePut(c *gin.Context) {
method messageTemplateGet (line 122) | func (rt *Router) messageTemplateGet(c *gin.Context) {
method messageTemplatesGet (line 140) | func (rt *Router) messageTemplatesGet(c *gin.Context) {
type evtMsgReq (line 173) | type evtMsgReq struct
method eventsMessage (line 180) | func (rt *Router) eventsMessage(c *gin.Context) {
FILE: center/router/router_metric_desc.go
method metricsDescGetFile (line 10) | func (rt *Router) metricsDescGetFile(c *gin.Context) {
method metricsDescGetMap (line 15) | func (rt *Router) metricsDescGetMap(c *gin.Context) {
FILE: center/router/router_metric_view.go
method metricViewGets (line 13) | func (rt *Router) metricViewGets(c *gin.Context) {
method metricViewAdd (line 19) | func (rt *Router) metricViewAdd(c *gin.Context) {
method metricViewDel (line 38) | func (rt *Router) metricViewDel(c *gin.Context) {
method metricViewPut (line 52) | func (rt *Router) metricViewPut(c *gin.Context) {
FILE: center/router/router_mute.go
method alertMuteGetsByBG (line 19) | func (rt *Router) alertMuteGetsByBG(c *gin.Context) {
method alertMuteGetsByGids (line 32) | func (rt *Router) alertMuteGetsByGids(c *gin.Context) {
method alertMuteGets (line 60) | func (rt *Router) alertMuteGets(c *gin.Context) {
method activeAlertMuteGets (line 74) | func (rt *Router) activeAlertMuteGets(c *gin.Context) {
method alertMuteAdd (line 79) | func (rt *Router) alertMuteAdd(c *gin.Context) {
type MuteTestForm (line 93) | type MuteTestForm struct
method alertMuteTryRun (line 99) | func (rt *Router) alertMuteTryRun(c *gin.Context) {
method alertMutePreview (line 144) | func (rt *Router) alertMutePreview(c *gin.Context) {
method alertMuteAddByService (line 165) | func (rt *Router) alertMuteAddByService(c *gin.Context) {
method alertMuteDel (line 173) | func (rt *Router) alertMuteDel(c *gin.Context) {
method alertMuteGet (line 182) | func (rt *Router) alertMuteGet(c *gin.Context) {
method alertMutePutByFE (line 189) | func (rt *Router) alertMutePutByFE(c *gin.Context) {
type alertMuteFieldForm (line 208) | type alertMuteFieldForm struct
method alertMutePutFields (line 213) | func (rt *Router) alertMutePutFields(c *gin.Context) {
FILE: center/router/router_mw.go
constant DefaultTokenKey (line 22) | DefaultTokenKey = "X-User-Token"
type AccessDetails (line 25) | type AccessDetails struct
method handleProxyUser (line 30) | func (rt *Router) handleProxyUser(c *gin.Context) *models.User {
method proxyAuth (line 61) | func (rt *Router) proxyAuth() gin.HandlerFunc {
method tokenAuth (line 72) | func (rt *Router) tokenAuth() gin.HandlerFunc {
method Auth (line 121) | func (rt *Router) Auth() gin.HandlerFunc {
method auth (line 125) | func (rt *Router) auth() gin.HandlerFunc {
method jwtMock (line 134) | func (rt *Router) jwtMock() gin.HandlerFunc {
method User (line 153) | func (rt *Router) User() gin.HandlerFunc {
method user (line 157) | func (rt *Router) user() gin.HandlerFunc {
method userGroupWrite (line 178) | func (rt *Router) userGroupWrite() gin.HandlerFunc {
method bgro (line 195) | func (rt *Router) bgro() gin.HandlerFunc {
method Bgrw (line 213) | func (rt *Router) Bgrw() gin.HandlerFunc {
method bgrw (line 217) | func (rt *Router) bgrw() gin.HandlerFunc {
method bgrwCheck (line 235) | func (rt *Router) bgrwCheck(c *gin.Context, bgid int64) {
method bgrwChecks (line 249) | func (rt *Router) bgrwChecks(c *gin.Context, bgids []int64) {
method bgroCheck (line 262) | func (rt *Router) bgroCheck(c *gin.Context, bgid int64) {
method Perm (line 276) | func (rt *Router) Perm(operation string) gin.HandlerFunc {
method perm (line 280) | func (rt *Router) perm(operation string) gin.HandlerFunc {
method admin (line 295) | func (rt *Router) admin() gin.HandlerFunc {
method extractTokenMetadata (line 326) | func (rt *Router) extractTokenMetadata(r *http.Request) (*AccessDetails,...
method extractToken (line 354) | func (rt *Router) extractToken(r *http.Request) string {
method createAuth (line 364) | func (rt *Router) createAuth(ctx context.Context, userIdentity string, t...
method fetchAuth (line 411) | func (rt *Router) fetchAuth(ctx context.Context, givenUuid string) (stri...
method deleteAuth (line 423) | func (rt *Router) deleteAuth(ctx context.Context, givenUuid string) error {
method deleteTokens (line 433) | func (rt *Router) deleteTokens(ctx context.Context, authD *AccessDetails...
method wrapJwtKey (line 452) | func (rt *Router) wrapJwtKey(key string) string {
method wrapIdTokenKey (line 456) | func (rt *Router) wrapIdTokenKey(userId int64) string {
method saveIdToken (line 461) | func (rt *Router) saveIdToken(ctx context.Context, userId int64, idToken...
method fetchIdToken (line 471) | func (rt *Router) fetchIdToken(ctx context.Context, userId int64) (strin...
method deleteIdToken (line 476) | func (rt *Router) deleteIdToken(ctx context.Context, userId int64) error {
type TokenDetails (line 480) | type TokenDetails struct
method createTokens (line 489) | func (rt *Router) createTokens(signingKey, userIdentity string) (*TokenD...
method verifyToken (line 524) | func (rt *Router) verifyToken(signingKey, tokenString string) (*jwt.Toke...
FILE: center/router/router_notification_record.go
type NotificationResponse (line 15) | type NotificationResponse struct
type SubRule (line 20) | type SubRule struct
type Record (line 26) | type Record struct
method notificationRecordAdd (line 35) | func (rt *Router) notificationRecordAdd(c *gin.Context) {
method notificationRecordList (line 44) | func (rt *Router) notificationRecordList(c *gin.Context) {
function buildNotificationResponse (line 53) | func buildNotificationResponse(ctx *ctx.Context, nl []*models.Notificati...
function checkChannel (line 163) | func checkChannel(channel string) bool {
function replaceLastEightChars (line 171) | func replaceLastEightChars(s string) string {
function fillUserNames (line 178) | func fillUserNames(ctx *ctx.Context, groupIdSet map[int64]struct{}) map[...
FILE: center/router/router_notify_channel.go
method notifyChannelsAdd (line 18) | func (rt *Router) notifyChannelsAdd(c *gin.Context) {
method notifyChannelsDel (line 54) | func (rt *Router) notifyChannelsDel(c *gin.Context) {
method notifyChannelPut (line 72) | func (rt *Router) notifyChannelPut(c *gin.Context) {
method notifyChannelGet (line 94) | func (rt *Router) notifyChannelGet(c *gin.Context) {
method notifyChannelGetBy (line 105) | func (rt *Router) notifyChannelGetBy(c *gin.Context) {
method notifyChannelsGet (line 119) | func (rt *Router) notifyChannelsGet(c *gin.Context) {
method notifyChannelsGetForNormalUser (line 127) | func (rt *Router) notifyChannelsGetForNormalUser(c *gin.Context) {
method notifyChannelIdentsGet (line 145) | func (rt *Router) notifyChannelIdentsGet(c *gin.Context) {
method flashDutyNotifyChannelsGet (line 168) | func (rt *Router) flashDutyNotifyChannelsGet(c *gin.Context) {
type flushDutyChannelsResponse (line 193) | type flushDutyChannelsResponse struct
type FlashDutyChannel (line 204) | type FlashDutyChannel struct
function getFlashDutyChannels (line 211) | func getFlashDutyChannels(integrationUrl string, jsonData []byte, timeou...
function parseIntegrationUrl (line 257) | func parseIntegrationUrl(urlStr string) (baseUrl string, integrationKey ...
method pagerDutyNotifyServicesGet (line 273) | func (rt *Router) pagerDutyNotifyServicesGet(c *gin.Context) {
method pagerDutyIntegrationKeyGet (line 302) | func (rt *Router) pagerDutyIntegrationKeyGet(c *gin.Context) {
type PagerDutyIntegration (line 323) | type PagerDutyIntegration struct
type PagerDutyService (line 330) | type PagerDutyService struct
function getPagerDutyServices (line 337) | func getPagerDutyServices(apiKey string, timeout time.Duration) ([]Pager...
function getPagerDutyIntegrationKey (line 387) | func getPagerDutyIntegrationKey(integrationUrl, apiKey string, timeout t...
FILE: center/router/router_notify_channel_test.go
function TestGetFlashDutyChannels (line 8) | func TestGetFlashDutyChannels(t *testing.T) {
FILE: center/router/router_notify_config.go
method webhookGets (line 20) | func (rt *Router) webhookGets(c *gin.Context) {
method webhookPuts (line 33) | func (rt *Router) webhookPuts(c *gin.Context) {
method notifyScriptGet (line 52) | func (rt *Router) notifyScriptGet(c *gin.Context) {
method notifyScriptPut (line 66) | func (rt *Router) notifyScriptPut(c *gin.Context) {
method notifyChannelGets (line 76) | func (rt *Router) notifyChannelGets(c *gin.Context) {
method notifyChannelPuts (line 89) | func (rt *Router) notifyChannelPuts(c *gin.Context) {
method notifyContactGets (line 113) | func (rt *Router) notifyContactGets(c *gin.Context) {
method notifyContactPuts (line 127) | func (rt *Router) notifyContactPuts(c *gin.Context) {
method notifyConfigGet (line 137) | func (rt *Router) notifyConfigGet(c *gin.Context) {
method notifyConfigPut (line 151) | func (rt *Router) notifyConfigPut(c *gin.Context) {
function SmtpValidate (line 177) | func SmtpValidate(text string) (aconf.SMTPConfig, error) {
type form (line 191) | type form struct
method attemptSendEmail (line 197) | func (rt *Router) attemptSendEmail(c *gin.Context) {
method notifyChannelConfigGets (line 217) | func (rt *Router) notifyChannelConfigGets(c *gin.Context) {
FILE: center/router/router_notify_rule.go
method notifyRulesAdd (line 19) | func (rt *Router) notifyRulesAdd(c *gin.Context) {
method notifyRulesDel (line 49) | func (rt *Router) notifyRulesDel(c *gin.Context) {
method notifyRulePut (line 70) | func (rt *Router) notifyRulePut(c *gin.Context) {
method notifyRuleGet (line 91) | func (rt *Router) notifyRuleGet(c *gin.Context) {
method notifyRulesGetByService (line 110) | func (rt *Router) notifyRulesGetByService(c *gin.Context) {
method notifyRulesGet (line 114) | func (rt *Router) notifyRulesGet(c *gin.Context) {
type NotifyTestForm (line 136) | type NotifyTestForm struct
method notifyTest (line 141) | func (rt *Router) notifyTest(c *gin.Context) {
function SendNotifyChannelMessage (line 171) | func SendNotifyChannelMessage(ctx *ctx.Context, userCache *memsto.UserCa...
type paramList (line 292) | type paramList struct
method notifyRuleCustomParamsGet (line 298) | func (rt *Router) notifyRuleCustomParamsGet(c *gin.Context) {
FILE: center/router/router_notify_tpl.go
method notifyTplGets (line 20) | func (rt *Router) notifyTplGets(c *gin.Context) {
method notifyTplUpdateContent (line 40) | func (rt *Router) notifyTplUpdateContent(c *gin.Context) {
method notifyTplUpdate (line 60) | func (rt *Router) notifyTplUpdate(c *gin.Context) {
function templateValidate (line 87) | func templateValidate(f models.NotifyTpl) error {
method notifyTplPreview (line 121) | func (rt *Router) notifyTplPreview(c *gin.Context) {
method notifyTplAdd (line 164) | func (rt *Router) notifyTplAdd(c *gin.Context) {
method notifyTplDel (line 185) | func (rt *Router) notifyTplDel(c *gin.Context) {
method messageTemplateGets (line 200) | func (rt *Router) messageTemplateGets(c *gin.Context) {
FILE: center/router/router_opensearch.go
method QueryOSIndices (line 12) | func (rt *Router) QueryOSIndices(c *gin.Context) {
method QueryOSFields (line 28) | func (rt *Router) QueryOSFields(c *gin.Context) {
method QueryOSVariable (line 44) | func (rt *Router) QueryOSVariable(c *gin.Context) {
FILE: center/router/router_proxy.go
type QueryFormItem (line 26) | type QueryFormItem struct
type BatchQueryForm (line 33) | type BatchQueryForm struct
method promBatchQueryRange (line 38) | func (rt *Router) promBatchQueryRange(c *gin.Context) {
function PromBatchQueryRange (line 46) | func PromBatchQueryRange(ctx context.Context, pc *prom.PromClientMap, f ...
type BatchInstantForm (line 73) | type BatchInstantForm struct
type InstantFormItem (line 78) | type InstantFormItem struct
method promBatchQueryInstant (line 83) | func (rt *Router) promBatchQueryInstant(c *gin.Context) {
function PromBatchQueryInstant (line 91) | func PromBatchQueryInstant(ctx context.Context, pc *prom.PromClientMap, ...
method dsProxy (line 112) | func (rt *Router) dsProxy(c *gin.Context) {
function transportGet (line 220) | func transportGet(dsid, newUpdatedAt int64) (http.RoundTripper, bool) {
function transportPut (line 248) | func transportPut(dsid, updatedat int64, tran http.RoundTripper) {
constant DatasourceTypePrometheus (line 256) | DatasourceTypePrometheus = "Prometheus"
constant DatasourceTypeVictoriaMetrics (line 257) | DatasourceTypeVictoriaMetrics = "VictoriaMetrics"
type deleteDatasourceSeriesForm (line 260) | type deleteDatasourceSeriesForm struct
method deleteDatasourceSeries (line 267) | func (rt *Router) deleteDatasourceSeries(c *gin.Context) {
FILE: center/router/router_query.go
type CheckDsPermFunc (line 16) | type CheckDsPermFunc
type QueryFrom (line 23) | type QueryFrom struct
type Query (line 28) | type Query struct
type Exp (line 35) | type Exp struct
type LogResp (line 40) | type LogResp struct
function QueryLogBatchConcurrently (line 45) | func QueryLogBatchConcurrently(anonymousAccess bool, ctx *gin.Context, f...
method QueryLogBatch (line 108) | func (rt *Router) QueryLogBatch(c *gin.Context) {
function QueryDataConcurrently (line 120) | func QueryDataConcurrently(anonymousAccess bool, ctx *gin.Context, f mod...
method QueryData (line 179) | func (rt *Router) QueryData(c *gin.Context) {
function QueryLogConcurrently (line 192) | func QueryLogConcurrently(anonymousAccess bool, ctx *gin.Context, f mode...
method QueryLogV2 (line 245) | func (rt *Router) QueryLogV2(c *gin.Context) {
method QueryLog (line 253) | func (rt *Router) QueryLog(c *gin.Context) {
FILE: center/router/router_recording_rule.go
method recordingRuleGets (line 15) | func (rt *Router) recordingRuleGets(c *gin.Context) {
method recordingRuleGetsByGids (line 24) | func (rt *Router) recordingRuleGetsByGids(c *gin.Context) {
method recordingRuleGetsByService (line 51) | func (rt *Router) recordingRuleGetsByService(c *gin.Context) {
method recordingRuleGet (line 56) | func (rt *Router) recordingRuleGet(c *gin.Context) {
method recordingRuleAddByFE (line 70) | func (rt *Router) recordingRuleAddByFE(c *gin.Context) {
method recordingRulePutByFE (line 107) | func (rt *Router) recordingRulePutByFE(c *gin.Context) {
method recordingRuleDel (line 128) | func (rt *Router) recordingRuleDel(c *gin.Context) {
type recordRuleFieldForm (line 137) | type recordRuleFieldForm struct
method recordingRulePutFields (line 142) | func (rt *Router) recordingRulePutFields(c *gin.Context) {
FILE: center/router/router_role.go
method rolesGets (line 14) | func (rt *Router) rolesGets(c *gin.Context) {
method permsGets (line 19) | func (rt *Router) permsGets(c *gin.Context) {
method roleAdd (line 37) | func (rt *Router) roleAdd(c *gin.Context) {
method rolePut (line 46) | func (rt *Router) rolePut(c *gin.Context) {
method roleDel (line 76) | func (rt *Router) roleDel(c *gin.Context) {
method roleGets (line 94) | func (rt *Router) roleGets(c *gin.Context) {
method allPerms (line 99) | func (rt *Router) allPerms(c *gin.Context) {
FILE: center/router/router_role_operation.go
method operationOfRole (line 13) | func (rt *Router) operationOfRole(c *gin.Context) {
method roleBindOperation (line 42) | func (rt *Router) roleBindOperation(c *gin.Context) {
method operations (line 60) | func (rt *Router) operations(c *gin.Context) {
FILE: center/router/router_saved_view.go
method savedViewGets (line 13) | func (rt *Router) savedViewGets(c *gin.Context) {
method savedViewAdd (line 62) | func (rt *Router) savedViewAdd(c *gin.Context) {
method savedViewPut (line 75) | func (rt *Router) savedViewPut(c *gin.Context) {
method savedViewDel (line 107) | func (rt *Router) savedViewDel(c *gin.Context) {
method savedViewFavoriteAdd (line 131) | func (rt *Router) savedViewFavoriteAdd(c *gin.Context) {
method savedViewFavoriteDel (line 139) | func (rt *Router) savedViewFavoriteDel(c *gin.Context) {
FILE: center/router/router_self.go
method selfProfileGet (line 15) | func (rt *Router) selfProfileGet(c *gin.Context) {
type selfProfileForm (line 23) | type selfProfileForm struct
method selfProfilePut (line 31) | func (rt *Router) selfProfilePut(c *gin.Context) {
type selfPasswordForm (line 55) | type selfPasswordForm struct
method selfPasswordPut (line 60) | func (rt *Router) selfPasswordPut(c *gin.Context) {
type tokenForm (line 87) | type tokenForm struct
method getToken (line 92) | func (rt *Router) getToken(c *gin.Context) {
method addToken (line 98) | func (rt *Router) addToken(c *gin.Context) {
method deleteToken (line 118) | func (rt *Router) deleteToken(c *gin.Context) {
FILE: center/router/router_server.go
method serversGet (line 12) | func (rt *Router) serversGet(c *gin.Context) {
method serverClustersGet (line 17) | func (rt *Router) serverClustersGet(c *gin.Context) {
method serverHeartbeat (line 22) | func (rt *Router) serverHeartbeat(c *gin.Context) {
method serversActive (line 29) | func (rt *Router) serversActive(c *gin.Context) {
FILE: center/router/router_source_token.go
method sourceTokenAdd (line 15) | func (rt *Router) sourceTokenAdd(c *gin.Context) {
FILE: center/router/router_target.go
type TargetQuery (line 23) | type TargetQuery struct
method targetGetsByHostFilter (line 29) | func (rt *Router) targetGetsByHostFilter(c *gin.Context) {
method targetGets (line 57) | func (rt *Router) targetGets(c *gin.Context) {
method downtimeFilter (line 183) | func (rt *Router) downtimeFilter(downtime int64) (option models.BuildTar...
method targetExtendInfoByIdent (line 215) | func (rt *Router) targetExtendInfoByIdent(c *gin.Context) {
method targetGetsByService (line 236) | func (rt *Router) targetGetsByService(c *gin.Context) {
method targetGetTags (line 241) | func (rt *Router) targetGetTags(c *gin.Context) {
type targetTagsForm (line 249) | type targetTagsForm struct
method targetBindTagsByFE (line 255) | func (rt *Router) targetBindTagsByFE(c *gin.Context) {
method targetBindTagsByService (line 275) | func (rt *Router) targetBindTagsByService(c *gin.Context) {
method targetBindTags (line 293) | func (rt *Router) targetBindTags(f targetTagsForm, failedIdents map[stri...
method validateTags (line 315) | func (rt *Router) validateTags(tags []string) error {
method addTagsToTarget (line 347) | func (rt *Router) addTagsToTarget(target *models.Target, tags []string) ...
method targetUnbindTagsByFE (line 358) | func (rt *Router) targetUnbindTagsByFE(c *gin.Context) {
method targetUnbindTagsByService (line 378) | func (rt *Router) targetUnbindTagsByService(c *gin.Context) {
method targetUnbindTags (line 396) | func (rt *Router) targetUnbindTags(f targetTagsForm, failedIdents map[st...
type targetNoteForm (line 415) | type targetNoteForm struct
method targetUpdateNote (line 421) | func (rt *Router) targetUpdateNote(c *gin.Context) {
method targetUpdateNoteByService (line 442) | func (rt *Router) targetUpdateNoteByService(c *gin.Context) {
type targetBgidForm (line 461) | type targetBgidForm struct
type targetBgidsForm (line 467) | type targetBgidsForm struct
function haveNeverGroupedIdent (line 475) | func haveNeverGroupedIdent(ctx *ctx.Context, idents []string) (bool, err...
method targetBindBgids (line 490) | func (rt *Router) targetBindBgids(c *gin.Context) {
method targetUpdateBgidByService (line 556) | func (rt *Router) targetUpdateBgidByService(c *gin.Context) {
type identsForm (line 575) | type identsForm struct
method targetDel (line 580) | func (rt *Router) targetDel(c *gin.Context) {
method targetDelByService (line 599) | func (rt *Router) targetDelByService(c *gin.Context) {
method checkTargetPerm (line 618) | func (rt *Router) checkTargetPerm(c *gin.Context, idents []string) {
method targetsOfAlertRule (line 628) | func (rt *Router) targetsOfAlertRule(c *gin.Context) {
method checkTargetsExistByIndent (line 646) | func (rt *Router) checkTargetsExistByIndent(idents []string) {
method targetsOfHostQuery (line 655) | func (rt *Router) targetsOfHostQuery(c *gin.Context) {
method targetStats (line 670) | func (rt *Router) targetStats(c *gin.Context) {
function usageBucket (line 755) | func usageBucket(val float64) string {
method targetUpdate (line 770) | func (rt *Router) targetUpdate(c *gin.Context) {
FILE: center/router/router_task.go
method taskGets (line 16) | func (rt *Router) taskGets(c *gin.Context) {
method taskGetsByGids (line 43) | func (rt *Router) taskGetsByGids(c *gin.Context) {
method taskRecordAdd (line 88) | func (rt *Router) taskRecordAdd(c *gin.Context) {
method taskAdd (line 94) | func (rt *Router) taskAdd(c *gin.Context) {
FILE: center/router/router_task_tpl.go
method taskTplGets (line 18) | func (rt *Router) taskTplGets(c *gin.Context) {
method taskTplGetsByGids (line 36) | func (rt *Router) taskTplGetsByGids(c *gin.Context) {
method taskTplGet (line 72) | func (rt *Router) taskTplGet(c *gin.Context) {
method taskTplGetByService (line 90) | func (rt *Router) taskTplGetByService(c *gin.Context) {
method taskTplGetsByService (line 103) | func (rt *Router) taskTplGetsByService(c *gin.Context) {
method taskTplStatistics (line 107) | func (rt *Router) taskTplStatistics(c *gin.Context) {
type taskTplForm (line 111) | type taskTplForm struct
method Verify (line 124) | func (f *taskTplForm) Verify() {
method taskTplAdd (line 136) | func (rt *Router) taskTplAdd(c *gin.Context) {
method taskTplPut (line 173) | func (rt *Router) taskTplPut(c *gin.Context) {
method taskTplDel (line 209) | func (rt *Router) taskTplDel(c *gin.Context) {
type tplTagsForm (line 230) | type tplTagsForm struct
method Verify (line 235) | func (f *tplTagsForm) Verify() {
method taskTplBindTags (line 264) | func (rt *Router) taskTplBindTags(c *gin.Context) {
method taskTplUnbindTags (line 285) | func (rt *Router) taskTplUnbindTags(c *gin.Context) {
FILE: center/router/router_tdengine.go
type databasesQueryForm (line 15) | type databasesQueryForm struct
method tdengineDatabases (line 20) | func (rt *Router) tdengineDatabases(c *gin.Context) {
type tablesQueryForm (line 34) | type tablesQueryForm struct
type Column (line 41) | type Column struct
method tdengineTables (line 48) | func (rt *Router) tdengineTables(c *gin.Context) {
type columnsQueryForm (line 67) | type columnsQueryForm struct
method tdengineColumns (line 74) | func (rt *Router) tdengineColumns(c *gin.Context) {
method QuerySqlTemplate (line 102) | func (rt *Router) QuerySqlTemplate(c *gin.Context) {
FILE: center/router/router_trace_logs.go
method traceLogsPage (line 19) | func (rt *Router) traceLogsPage(c *gin.Context) {
method traceLogsJSON (line 45) | func (rt *Router) traceLogsJSON(c *gin.Context) {
method getTraceLogs (line 62) | func (rt *Router) getTraceLogs(traceId string) ([]string, string, error) {
method forwardTraceLogs (line 100) | func (rt *Router) forwardTraceLogs(node, traceId string) ([]string, stri...
FILE: center/router/router_user.go
method userBusiGroupsGets (line 19) | func (rt *Router) userBusiGroupsGets(c *gin.Context) {
method userFindAll (line 41) | func (rt *Router) userFindAll(c *gin.Context) {
method userGets (line 46) | func (rt *Router) userGets(c *gin.Context) {
type userAddForm (line 84) | type userAddForm struct
method userAddPost (line 95) | func (rt *Router) userAddPost(c *gin.Context) {
method userProfileGet (line 136) | func (rt *Router) userProfileGet(c *gin.Context) {
type userProfileForm (line 141) | type userProfileForm struct
method userProfilePutByService (line 149) | func (rt *Router) userProfilePutByService(c *gin.Context) {
method userProfilePut (line 173) | func (rt *Router) userProfilePut(c *gin.Context) {
type userPasswordForm (line 201) | type userPasswordForm struct
method userPasswordPut (line 205) | func (rt *Router) userPasswordPut(c *gin.Context) {
method userDel (line 228) | func (rt *Router) userDel(c *gin.Context) {
method installDateGet (line 251) | func (rt *Router) installDateGet(c *gin.Context) {
method usersPhoneEncrypt (line 269) | func (rt *Router) usersPhoneEncrypt(c *gin.Context) {
method usersPhoneDecryptRefresh (line 358) | func (rt *Router) usersPhoneDecryptRefresh(c *gin.Context) {
method usersPhoneDecrypt (line 369) | func (rt *Router) usersPhoneDecrypt(c *gin.Context) {
function isPhoneEncrypted (line 470) | func isPhoneEncrypted(phone string) bool {
FILE: center/router/router_user_group.go
method checkBusiGroupPerm (line 16) | func (rt *Router) checkBusiGroupPerm(c *gin.Context) {
method userGroupGets (line 24) | func (rt *Router) userGroupGets(c *gin.Context) {
method userGroupGetsByService (line 37) | func (rt *Router) userGroupGetsByService(c *gin.Context) {
method userGroupMemberGetsByService (line 71) | func (rt *Router) userGroupMemberGetsByService(c *gin.Context) {
type userGroupForm (line 76) | type userGroupForm struct
method userGroupAdd (line 82) | func (rt *Router) userGroupAdd(c *gin.Context) {
method userGroupPut (line 111) | func (rt *Router) userGroupPut(c *gin.Context) {
method userGroupGet (line 143) | func (rt *Router) userGroupGet(c *gin.Context) {
method userGroupDel (line 158) | func (rt *Router) userGroupDel(c *gin.Context) {
method userGroupMemberAdd (line 174) | func (rt *Router) userGroupMemberAdd(c *gin.Context) {
method userGroupMemberDel (line 200) | func (rt *Router) userGroupMemberDel(c *gin.Context) {
FILE: center/router/router_user_variable_config.go
method userVariableConfigGets (line 13) | func (rt *Router) userVariableConfigGets(context *gin.Context) {
method userVariableConfigAdd (line 17) | func (rt *Router) userVariableConfigAdd(context *gin.Context) {
method userVariableConfigPut (line 32) | func (rt *Router) userVariableConfigPut(context *gin.Context) {
method userVariableConfigDel (line 49) | func (rt *Router) userVariableConfigDel(context *gin.Context) {
method userVariableGetDecryptByService (line 67) | func (rt *Router) userVariableGetDecryptByService(context *gin.Context) {
FILE: center/sso/init.go
type SsoClient (line 25) | type SsoClient struct
method reload (line 224) | func (s *SsoClient) reload(ctx *ctx.Context) error {
method Reload (line 324) | func (s *SsoClient) Reload(ctx *ctx.Context) {
constant LDAP (line 37) | LDAP = `
constant OAuth2 (line 64) | OAuth2 = `
constant CAS (line 88) | CAS = `
constant OIDC (line 105) | OIDC = `
function Init (line 124) | func Init(center cconf.Center, ctx *ctx.Context, configCache *memsto.Con...
FILE: center/sso/sync.go
method SyncSsoUsers (line 10) | func (s *SsoClient) SyncSsoUsers(ctx *ctx.Context) {
method loopSyncSsoUsers (line 22) | func (s *SsoClient) loopSyncSsoUsers(ctx *ctx.Context) {
FILE: cli/cli.go
function Upgrade (line 7) | func Upgrade(configFile string) error {
FILE: cli/upgrade/config.go
type Config (line 13) | type Config struct
type ClusterOptions (line 18) | type ClusterOptions struct
function Parse (line 36) | func Parse(fpath string, configPtr *Config) error {
FILE: cli/upgrade/upgrade.go
function Upgrade (line 12) | func Upgrade(configFile string) error {
FILE: cli/upgrade/upgrade.sql
type `datasource` (line 55) | CREATE TABLE `datasource`
type `builtin_cate` (line 79) | CREATE TABLE `builtin_cate` (
type `notify_tpl` (line 86) | CREATE TABLE `notify_tpl` (
type `sso_config` (line 95) | CREATE TABLE `sso_config` (
FILE: cmd/alert/main.go
function main (line 24) | func main() {
function printEnv (line 63) | func printEnv() {
FILE: cmd/center/main.go
function main (line 25) | func main() {
function printEnv (line 66) | func printEnv() {
FILE: cmd/cli/main.go
function main (line 18) | func main() {
FILE: cmd/edge/edge.go
function Initialize (line 30) | func Initialize(configDir string, cryptoKey string) (func(), error) {
FILE: cmd/edge/main.go
function main (line 23) | func main() {
function printEnv (line 62) | func printEnv() {
FILE: cmd/pushgw/main.go
function main (line 24) | func main() {
function printEnv (line 63) | func printEnv() {
FILE: conf/conf.go
type ConfigType (line 19) | type ConfigType struct
type CenterApi (line 33) | type CenterApi struct
type GlobalConfig (line 40) | type GlobalConfig struct
type Ibex (line 44) | type Ibex struct
type Output (line 50) | type Output struct
function InitConfig (line 55) | func InitConfig(configDir, cryptoKey string) (*ConfigType, error) {
function GetOutboundIP (line 94) | func GetOutboundIP() net.IP {
FILE: conf/crypto.go
function decryptConfig (line 9) | func decryptConfig(config *ConfigType, cryptoKey string) error {
FILE: cron/clean_notify_record.go
function cleanNotifyRecord (line 13) | func cleanNotifyRecord(ctx *ctx.Context, day int) {
function CleanNotifyRecord (line 23) | func CleanNotifyRecord(ctx *ctx.Context, day int) {
FILE: cron/clean_pipeline_execution.go
constant defaultBatchSize (line 14) | defaultBatchSize = 100
constant defaultSleepMs (line 15) | defaultSleepMs = 10
function cleanPipelineExecutionInBatches (line 19) | func cleanPipelineExecutionInBatches(ctx *ctx.Context, day int) {
function CleanPipelineExecution (line 50) | func CleanPipelineExecution(ctx *ctx.Context, day int) {
FILE: datasource/ck/clickhouse.go
constant CKType (line 20) | CKType = "ck"
constant TimeFieldFormatEpochMilli (line 22) | TimeFieldFormatEpochMilli = "epoch_millis"
constant TimeFieldFormatEpochSecond (line 23) | TimeFieldFormatEpochSecond = "epoch_second"
constant DefaultLimit (line 25) | DefaultLimit = 500
function init (line 53) | func init() {
type CKShard (line 57) | type CKShard struct
type QueryParam (line 65) | type QueryParam struct
type Clickhouse (line 78) | type Clickhouse struct
method Init (line 82) | func (c *Clickhouse) Init(settings map[string]interface{}) (datasource...
method InitClient (line 88) | func (c *Clickhouse) InitClient() error {
method Validate (line 92) | func (c *Clickhouse) Validate(ctx context.Context) error {
method Equal (line 114) | func (c *Clickhouse) Equal(p datasource.Datasource) bool {
method MakeLogQuery (line 150) | func (c *Clickhouse) MakeLogQuery(ctx context.Context, query interface...
method MakeTSQuery (line 154) | func (c *Clickhouse) MakeTSQuery(ctx context.Context, query interface{...
method QueryMapData (line 158) | func (c *Clickhouse) QueryMapData(ctx context.Context, query interface...
method QueryData (line 162) | func (c *Clickhouse) QueryData(ctx context.Context, query interface{})...
method QueryLog (line 198) | func (c *Clickhouse) QueryLog(ctx context.Context, query interface{}) ...
function getLimit (line 228) | func getLimit(rowLen, pLimit int) int {
FILE: datasource/commons/eslike/eslike.go
type FixedField (line 23) | type FixedField
constant FieldIndex (line 26) | FieldIndex FixedField = "_index"
constant FieldId (line 27) | FieldId FixedField = "_id"
constant LabelSeparator (line 32) | LabelSeparator = "\x1e"
type Query (line 34) | type Query struct
type SortField (line 57) | type SortField struct
type SearchAfter (line 62) | type SearchAfter struct
type MetricAggr (line 67) | type MetricAggr struct
type GroupBy (line 73) | type GroupBy struct
type SearchFunc (line 85) | type SearchFunc
type QueryFieldsFunc (line 86) | type QueryFieldsFunc
type GroupByCate (line 89) | type GroupByCate
constant Filters (line 92) | Filters GroupByCate = "filters"
constant Histogram (line 93) | Histogram GroupByCate = "histogram"
constant Terms (line 94) | Terms GroupByCate = "terms"
type Param (line 98) | type Param struct
type MetricPtr (line 103) | type MetricPtr struct
function IterGetMap (line 107) | func IterGetMap(m, ret map[string]interface{}, prefixKey string) {
function TransferData (line 124) | func TransferData(metric, ref string, m map[string][][]float64) []models...
function GetQueryString (line 157) | func GetQueryString(filter string, q *elastic.RangeQuery) *elastic.BoolQ...
function getUnixTs (line 172) | func getUnixTs(timeStr string) int64 {
function GetBuckets (line 186) | func GetBuckets(labelKey string, keys []string, arr []interface{}, metri...
function MakeLogQuery (line 292) | func MakeLogQuery(ctx context.Context, query interface{}, eventTags []st...
function MakeTSQuery (line 319) | func MakeTSQuery(ctx context.Context, query interface{}, eventTags []str...
function SetEsIndexPatternCacheType (line 347) | func SetEsIndexPatternCacheType(c *memsto.EsIndexPatternCacheType) {
function GetEsIndexPatternCacheType (line 351) | func GetEsIndexPatternCacheType() *memsto.EsIndexPatternCacheType {
function QueryData (line 355) | func QueryData(ctx context.Context, queryParam interface{}, cliTimeout i...
function checkShardFailures (line 628) | func checkShardFailures(ctx context.Context, shards *elastic.ShardsInfo,...
function HitFilter (line 661) | func HitFilter(typ string) bool {
function QueryLog (line 670) | func QueryLog(ctx context.Context, queryParam interface{}, timeout int64...
FILE: datasource/datasource.go
type DatasourceType (line 11) | type DatasourceType struct
type Keys (line 18) | type Keys struct
function init (line 27) | func init() {
type NewDatasourceFn (line 79) | type NewDatasourceFn
type Datasource (line 83) | type Datasource interface
function RegisterDatasource (line 97) | func RegisterDatasource(typ string, p Datasource) {
function GetDatasourceByType (line 104) | func GetDatasourceByType(typ string, settings map[string]interface{}) (D...
type DatasourceInfo (line 119) | type DatasourceInfo struct
FILE: datasource/doris/doris.go
constant DorisType (line 22) | DorisType = "doris"
function init (line 25) | func init() {
type Doris (line 29) | type Doris struct
method InitClient (line 48) | func (d *Doris) InitClient() error {
method Init (line 58) | func (d *Doris) Init(settings map[string]interface{}) (datasource.Data...
method Validate (line 64) | func (d *Doris) Validate(ctx context.Context) error {
method Equal (line 77) | func (d *Doris) Equal(p datasource.Datasource) bool {
method MakeLogQuery (line 99) | func (d *Doris) MakeLogQuery(ctx context.Context, query interface{}, e...
method MakeTSQuery (line 103) | func (d *Doris) MakeTSQuery(ctx context.Context, query interface{}, ev...
method QueryMapData (line 107) | func (d *Doris) QueryMapData(ctx context.Context, query interface{}) (...
method QueryData (line 111) | func (d *Doris) QueryData(ctx context.Context, query interface{}) ([]m...
method QueryLog (line 182) | func (d *Doris) QueryLog(ctx context.Context, query interface{}) ([]in...
method DescribeTable (line 224) | func (d *Doris) DescribeTable(ctx context.Context, query interface{}) ...
type QueryParam (line 33) | type QueryParam struct
FILE: datasource/es/es.go
constant ESType (line 27) | ESType = "elasticsearch"
type Elasticsearch (line 30) | type Elasticsearch struct
method Init (line 59) | func (e *Elasticsearch) Init(settings map[string]interface{}) (datasou...
method InitClient (line 65) | func (e *Elasticsearch) InitClient() error {
method Equal (line 117) | func (e *Elasticsearch) Equal(other datasource.Datasource) bool {
method Validate (line 147) | func (e *Elasticsearch) Validate(ctx context.Context) (err error) {
method MakeLogQuery (line 178) | func (e *Elasticsearch) MakeLogQuery(ctx context.Context, query interf...
method MakeTSQuery (line 182) | func (e *Elasticsearch) MakeTSQuery(ctx context.Context, query interfa...
method QueryData (line 186) | func (e *Elasticsearch) QueryData(ctx context.Context, queryParam inte...
method QueryIndices (line 199) | func (e *Elasticsearch) QueryIndices() ([]string, error) {
method QueryFields (line 205) | func (e *Elasticsearch) QueryFields(indexes []string) ([]string, error) {
method QueryLog (line 251) | func (e *Elasticsearch) QueryLog(ctx context.Context, queryParam inter...
method QueryFieldValue (line 277) | func (e *Elasticsearch) QueryFieldValue(indexes []string, field string...
method Test (line 306) | func (e *Elasticsearch) Test(ctx context.Context) (err error) {
method QueryMapData (line 359) | func (e *Elasticsearch) QueryMapData(ctx context.Context, query interf...
type TLS (line 45) | type TLS struct
type BasicAuth (line 49) | type BasicAuth struct
function init (line 55) | func init() {
function getFieldType (line 341) | func getFieldType(key string, m map[string]interface{}) string {
FILE: datasource/mysql/mysql.go
constant MySQLType (line 23) | MySQLType = "mysql"
function init (line 26) | func init() {
type MySQL (line 30) | type MySQL struct
method InitClient (line 44) | func (m *MySQL) InitClient() error {
method Init (line 54) | func (m *MySQL) Init(settings map[string]interface{}) (datasource.Data...
method Validate (line 60) | func (m *MySQL) Validate(ctx context.Context) error {
method Equal (line 73) | func (m *MySQL) Equal(p datasource.Datasource) bool {
method MakeLogQuery (line 122) | func (m *MySQL) MakeLogQuery(ctx context.Context, query interface{}, e...
method MakeTSQuery (line 126) | func (m *MySQL) MakeTSQuery(ctx context.Context, query interface{}, ev...
method QueryMapData (line 130) | func (m *MySQL) QueryMapData(ctx context.Context, query interface{}) (...
method QueryData (line 134) | func (m *MySQL) QueryData(ctx context.Context, query interface{}) ([]m...
method QueryLog (line 185) | func (m *MySQL) QueryLog(ctx context.Context, query interface{}) ([]in...
method DescribeTable (line 223) | func (m *MySQL) DescribeTable(ctx context.Context, query interface{}) ...
type QueryParam (line 34) | type QueryParam struct
FILE: datasource/opensearch/opensearch.go
constant OpenSearchType (line 30) | OpenSearchType = "opensearch"
type OpenSearch (line 33) | type OpenSearch struct
method Init (line 61) | func (os *OpenSearch) Init(settings map[string]interface{}) (datasourc...
method InitClient (line 67) | func (os *OpenSearch) InitClient() error {
method Equal (line 115) | func (os *OpenSearch) Equal(other datasource.Datasource) bool {
method Validate (line 146) | func (os *OpenSearch) Validate(ctx context.Context) (err error) {
method MakeLogQuery (line 182) | func (os *OpenSearch) MakeLogQuery(ctx context.Context, query interfac...
method MakeTSQuery (line 186) | func (os *OpenSearch) MakeTSQuery(ctx context.Context, query interface...
method QueryData (line 233) | func (os *OpenSearch) QueryData(ctx context.Context, queryParam interf...
method QueryIndices (line 242) | func (os *OpenSearch) QueryIndices() ([]string, error) {
method QueryFields (line 276) | func (os *OpenSearch) QueryFields(indices []string) ([]string, error) {
method QueryLog (line 363) | func (os *OpenSearch) QueryLog(ctx context.Context, queryParam interfa...
method QueryFieldValue (line 372) | func (os *OpenSearch) QueryFieldValue(indexes []string, field string, ...
method QueryMapData (line 399) | func (os *OpenSearch) QueryMapData(ctx context.Context, query interfac...
type TLS (line 47) | type TLS struct
type BasicAuth (line 51) | type BasicAuth struct
function init (line 57) | func init() {
function search (line 190) | func search(ctx context.Context, indices []string, source interface{}, t...
function propertyMappingRange (line 332) | func propertyMappingRange(v interface{}, depth int) (fields []string) {
FILE: datasource/postgresql/postgresql.go
constant PostgreSQLType (line 24) | PostgreSQLType = "pgsql"
function init (line 31) | func init() {
type PostgreSQL (line 35) | type PostgreSQL struct
method InitClient (line 49) | func (p *PostgreSQL) InitClient() error {
method Init (line 62) | func (p *PostgreSQL) Init(settings map[string]interface{}) (datasource...
method Validate (line 68) | func (p *PostgreSQL) Validate(ctx context.Context) error {
method Equal (line 81) | func (p *PostgreSQL) Equal(d datasource.Datasource) bool {
method ShowDatabases (line 130) | func (p *PostgreSQL) ShowDatabases(ctx context.Context) ([]string, err...
method ShowTables (line 134) | func (p *PostgreSQL) ShowTables(ctx context.Context, database string) ...
method MakeLogQuery (line 149) | func (p *PostgreSQL) MakeLogQuery(ctx context.Context, query interface...
method MakeTSQuery (line 153) | func (p *PostgreSQL) MakeTSQuery(ctx context.Context, query interface{...
method QueryMapData (line 157) | func (p *PostgreSQL) QueryMapData(ctx context.Context, query interface...
method QueryData (line 161) | func (p *PostgreSQL) QueryData(ctx context.Context, query interface{})...
method QueryLog (line 220) | func (p *PostgreSQL) QueryLog(ctx context.Context, query interface{}) ...
method DescribeTable (line 265) | func (p *PostgreSQL) DescribeTable(ctx context.Context, query interfac...
type QueryParam (line 39) | type QueryParam struct
function parseDBName (line 281) | func parseDBName(sql string) (db string, err error) {
function formatSQLDatabaseNameWithRegex (line 292) | func formatSQLDatabaseNameWithRegex(sql string) string {
function extractColumns (line 300) | func extractColumns(sql string) ([]string, error) {
function splitColumns (line 326) | func splitColumns(columnsString string) []string {
FILE: datasource/prom/prom.go
type Prometheus (line 3) | type Prometheus struct
FILE: datasource/tdengine/tdengine.go
constant TDEngineType (line 25) | TDEngineType = "tdengine"
type TDengine (line 28) | type TDengine struct
method Init (line 51) | func (td *TDengine) Init(settings map[string]interface{}) (datasource....
method InitClient (line 57) | func (td *TDengine) InitClient() error {
method Equal (line 62) | func (td *TDengine) Equal(other datasource.Datasource) bool {
method Validate (line 110) | func (td *TDengine) Validate(ctx context.Context) (err error) {
method MakeLogQuery (line 114) | func (td *TDengine) MakeLogQuery(ctx context.Context, query interface{...
method MakeTSQuery (line 118) | func (td *TDengine) MakeTSQuery(ctx context.Context, query interface{}...
method QueryData (line 122) | func (td *TDengine) QueryData(ctx context.Context, queryParam interfac...
method QueryLog (line 126) | func (td *TDengine) QueryLog(ctx context.Context, queryParam interface...
method QueryMapData (line 171) | func (td *TDengine) QueryMapData(ctx context.Context, query interface{...
method Query (line 175) | func (td *TDengine) Query(ctx context.Context, query interface{}, dela...
type TdengineQuery (line 32) | type TdengineQuery struct
type Keys (line 41) | type Keys struct
function init (line 47) | func init() {
function ConvertToTStData (line 222) | func ConvertToTStData(src td.APIResponse, key Keys, ref string) ([]model...
function interfaceToFloat64 (line 357) | func interfaceToFloat64(input interface{}) (float64, error) {
function parseTimeString (line 385) | func parseTimeString(ts string) (time.Time, error) {
function ConvertToTable (line 451) | func ConvertToTable(src td.APIResponse) []interface{} {
FILE: datasource/victorialogs/victorialogs.go
constant VictoriaLogsType (line 20) | VictoriaLogsType = "victorialogs"
type VictoriaLogs (line 24) | type VictoriaLogs struct
method Init (line 49) | func (vl *VictoriaLogs) Init(settings map[string]interface{}) (datasou...
method InitClient (line 56) | func (vl *VictoriaLogs) InitClient() error {
method Validate (line 65) | func (vl *VictoriaLogs) Validate(ctx context.Context) error {
method Equal (line 95) | func (vl *VictoriaLogs) Equal(other datasource.Datasource) bool {
method QueryLog (line 110) | func (vl *VictoriaLogs) QueryLog(ctx context.Context, queryParam inter...
method QueryData (line 138) | func (vl *VictoriaLogs) QueryData(ctx context.Context, queryParam inte...
method queryDataInstant (line 152) | func (vl *VictoriaLogs) queryDataInstant(ctx context.Context, param *Q...
method queryDataRange (line 170) | func (vl *VictoriaLogs) queryDataRange(ctx context.Context, param *Que...
method MakeLogQuery (line 256) | func (vl *VictoriaLogs) MakeLogQuery(ctx context.Context, query interf...
method MakeTSQuery (line 284) | func (vl *VictoriaLogs) MakeTSQuery(ctx context.Context, query interfa...
method QueryMapData (line 307) | func (vl *VictoriaLogs) QueryMapData(ctx context.Context, query interf...
type Query (line 29) | type Query struct
method IsInstantQuery (line 40) | func (q *Query) IsInstantQuery() bool {
function init (line 44) | func init() {
function convertPrometheusInstantToDataResp (line 193) | func convertPrometheusInstantToDataResp(resp *victorialogs.PrometheusRes...
function convertPrometheusRangeToDataResp (line 224) | func convertPrometheusRangeToDataResp(resp *victorialogs.PrometheusRespo...
FILE: docker/compose-bridge/etc-nightingale/script/notify.bak.py
class Sender (line 27) | class Sender(object):
method send_email (line 29) | def send_email(cls, payload):
method send_wecom (line 60) | def send_wecom(cls, payload):
method send_dingtalk (line 91) | def send_dingtalk(cls, payload):
method send_feishu (line 137) | def send_feishu(cls, payload):
method send_sms (line 176) | def send_sms(cls, payload):
method send_voice (line 186) | def send_voice(cls, payload):
function main (line 195) | def main():
function hello (line 207) | def hello():
FILE: docker/compose-bridge/etc-nightingale/script/notify.py
class Sender (line 6) | class Sender(object):
method send_email (line 8) | def send_email(cls, payload):
method send_wecom (line 13) | def send_wecom(cls, payload):
method send_dingtalk (line 18) | def send_dingtalk(cls, payload):
method send_feishu (line 23) | def send_feishu(cls, payload):
method send_mm (line 28) | def send_mm(cls, payload):
method send_sms (line 33) | def send_sms(cls, payload):
method send_voice (line 43) | def send_voice(cls, payload):
function main (line 52) | def main():
function hello (line 64) | def hello():
FILE: docker/compose-bridge/etc-nightingale/script/notify_feishu.py
class Sender (line 7) | class Sender(object):
method send_email (line 9) | def send_email(cls, payload):
method send_wecom (line 14) | def send_wecom(cls, payload):
method send_dingtalk (line 19) | def send_dingtalk(cls, payload):
method send_ifeishu (line 24) | def send_ifeishu(cls, payload):
method send_mm (line 59) | def send_mm(cls, payload):
method send_sms (line 64) | def send_sms(cls, payload):
method send_voice (line 68) | def send_voice(cls, payload):
function main (line 71) | def main():
function hello (line 83) | def hello():
FILE: docker/compose-bridge/etc-nightingale/script/rule_converter.py
function convert_interval (line 12) | def convert_interval(interval):
function convert_alert (line 24) | def convert_alert(rule, interval):
function convert_record (line 92) | def convert_record(rule, interval):
function deal_group (line 127) | def deal_group(group):
function deal_configmap (line 169) | def deal_configmap(rule_configmap):
function main (line 184) | def main():
FILE: docker/compose-host-network-metric-log/etc-nightingale/script/notify.bak.py
class Sender (line 27) | class Sender(object):
method send_email (line 29) | def send_email(cls, payload):
method send_wecom (line 60) | def send_wecom(cls, payload):
method send_dingtalk (line 91) | def send_dingtalk(cls, payload):
method send_feishu (line 137) | def send_feishu(cls, payload):
method send_sms (line 176) | def send_sms(cls, payload):
method send_voice (line 186) | def send_voice(cls, payload):
function main (line 195) | def main():
function hello (line 207) | def hello():
FILE: docker/compose-host-network-metric-log/etc-nightingale/script/notify.py
class Sender (line 6) | class Sender(object):
method send_email (line 8) | def send_email(cls, payload):
method send_wecom (line 13) | def send_wecom(cls, payload):
method send_dingtalk (line 18) | def send_dingtalk(cls, payload):
method send_feishu (line 23) | def send_feishu(cls, payload):
method send_mm (line 28) | def send_mm(cls, payload):
method send_sms (line 33) | def send_sms(cls, payload):
method send_voice (line 43) | def send_voice(cls, payload):
function main (line 52) | def main():
function hello (line 64) | def hello():
FILE: docker/compose-host-network-metric-log/etc-nightingale/script/notify_feishu.py
class Sender (line 7) | class Sender(object):
method send_email (line 9) | def send_email(cls, payload):
method send_wecom (line 14) | def send_wecom(cls, payload):
method send_dingtalk (line 19) | def send_dingtalk(cls, payload):
method send_ifeishu (line 24) | def send_ifeishu(cls, payload):
method send_mm (line 59) | def send_mm(cls, payload):
method send_sms (line 64) | def send_sms(cls, payload):
method send_voice (line 68) | def send_voice(cls, payload):
function main (line 71) | def main():
function hello (line 83) | def hello():
FILE: docker/compose-host-network-metric-log/etc-nightingale/script/rule_converter.py
function convert_interval (line 12) | def convert_interval(interval):
function convert_alert (line 24) | def convert_alert(rule, interval):
function convert_record (line 92) | def convert_record(rule, interval):
function deal_group (line 127) | def deal_group(group):
function deal_configmap (line 169) | def deal_configmap(rule_configmap):
function main (line 184) | def main():
FILE: docker/compose-host-network/etc-nightingale/script/notify.bak.py
class Sender (line 27) | class Sender(object):
method send_email (line 29) | def send_email(cls, payload):
method send_wecom (line 60) | def send_wecom(cls, payload):
method send_dingtalk (line 91) | def send_dingtalk(cls, payload):
method send_feishu (line 137) | def send_feishu(cls, payload):
method send_sms (line 176) | def send_sms(cls, payload):
method send_voice (line 186) | def send_voice(cls, payload):
function main (line 195) | def main():
function hello (line 207) | def hello():
FILE: docker/compose-host-network/etc-nightingale/script/notify.py
class Sender (line 6) | class Sender(object):
method send_email (line 8) | def send_email(cls, payload):
method send_wecom (line 13) | def send_wecom(cls, payload):
method send_dingtalk (line 18) | def send_dingtalk(cls, payload):
method send_feishu (line 23) | def send_feishu(cls, payload):
method send_mm (line 28) | def send_mm(cls, payload):
method send_sms (line 33) | def send_sms(cls, payload):
method send_voice (line 43) | def send_voice(cls, payload):
function main (line 52) | def main():
function hello (line 64) | def hello():
FILE: docker/compose-host-network/etc-nightingale/script/notify_feishu.py
class Sender (line 7) | class Sender(object):
method send_email (line 9) | def send_email(cls, payload):
method send_wecom (line 14) | def send_wecom(cls, payload):
method send_dingtalk (line 19) | def send_dingtalk(cls, payload):
method send_ifeishu (line 24) | def send_ifeishu(cls, payload):
method send_mm (line 59) | def send_mm(cls, payload):
method send_sms (line 64) | def send_sms(cls, payload):
method send_voice (line 68) | def send_voice(cls, payload):
function main (line 71) | def main():
function hello (line 83) | def hello():
FILE: docker/compose-host-network/etc-nightingale/script/rule_converter.py
function convert_interval (line 12) | def convert_interval(interval):
function convert_alert (line 24) | def convert_alert(rule, interval):
function convert_record (line 92) | def convert_record(rule, interval):
function deal_group (line 127) | def deal_group(group):
function deal_configmap (line 169) | def deal_configmap(rule_configmap):
function main (line 184) | def main():
FILE: docker/compose-postgres/initsql_for_postgres/a-n9e-for-Postgres.sql
type users (line 1) | CREATE TABLE users (
type user_group (line 32) | CREATE TABLE user_group (
type user_group_create_by_idx (line 42) | CREATE INDEX user_group_create_by_idx ON user_group (create_by)
type user_group_update_at_idx (line 43) | CREATE INDEX user_group_update_at_idx ON user_group (update_at)
type user_group_member (line 47) | CREATE TABLE user_group_member (
type user_group_member_group_id_idx (line 53) | CREATE INDEX user_group_member_group_id_idx ON user_group_member (group_id)
type user_group_member_user_id_idx (line 54) | CREATE INDEX user_group_member_user_id_idx ON user_group_member (user_id)
type configs (line 58) | CREATE TABLE configs (
type role (line 73) | CREATE TABLE role (
type role_operation (line 85) | CREATE TABLE role_operation(
type role_operation_role_name_idx (line 91) | CREATE INDEX role_operation_role_name_idx ON role_operation (role_name)
type role_operation_operation_idx (line 92) | CREATE INDEX role_operation_operation_idx ON role_operation (operation)
type busi_group (line 165) | CREATE TABLE busi_group (
type busi_group_member (line 181) | CREATE TABLE busi_group_member (
type busi_group_member_busi_group_id_idx (line 188) | CREATE INDEX busi_group_member_busi_group_id_idx ON busi_group_member (b...
type busi_group_member_user_group_id_idx (line 189) | CREATE INDEX busi_group_member_user_group_id_idx ON busi_group_member (u...
type board (line 198) | CREATE TABLE board (
type board_ident_idx (line 216) | CREATE INDEX board_ident_idx ON board (ident)
type board_payload (line 227) | CREATE TABLE board_payload (
type dashboard (line 235) | CREATE TABLE dashboard (
type chart_group (line 254) | CREATE TABLE chart_group (
type chart_group_dashboard_id_idx (line 261) | CREATE INDEX chart_group_dashboard_id_idx ON chart_group (dashboard_id)
type chart (line 264) | CREATE TABLE chart (
type chart_group_id_idx (line 271) | CREATE INDEX chart_group_id_idx ON chart (group_id)
type chart_share (line 275) | CREATE TABLE chart_share (
type chart_share_create_at_idx (line 284) | CREATE INDEX chart_share_create_at_idx ON chart_share (create_at)
type alert_rule (line 287) | CREATE TABLE alert_rule (
type alert_rule_group_id_idx (line 327) | CREATE INDEX alert_rule_group_id_idx ON alert_rule (group_id)
type alert_rule_update_at_idx (line 328) | CREATE INDEX alert_rule_update_at_idx ON alert_rule (update_at)
type alert_mute (line 351) | CREATE TABLE alert_mute (
type alert_mute_group_id_idx (line 373) | CREATE INDEX alert_mute_group_id_idx ON alert_mute (group_id)
type alert_mute_update_at_idx (line 374) | CREATE INDEX alert_mute_update_at_idx ON alert_mute (update_at)
type alert_subscribe (line 383) | CREATE TABLE alert_subscribe (
type alert_subscribe (line 414) | CREATE INDEX ON alert_subscribe (update_at)
type alert_subscribe (line 415) | CREATE INDEX ON alert_subscribe (group_id)
type target (line 431) | CREATE TABLE target (
type target (line 447) | CREATE INDEX ON target (group_id)
type idx_host_ip (line 448) | CREATE INDEX idx_host_ip ON target (host_ip)
type idx_agent_version (line 449) | CREATE INDEX idx_agent_version ON target (agent_version)
type idx_engine_name (line 450) | CREATE INDEX idx_engine_name ON target (engine_name)
type idx_os (line 451) | CREATE INDEX idx_os ON target (os)
type metric_view (line 463) | CREATE TABLE metric_view (
type metric_view_create_by_idx (line 473) | CREATE INDEX metric_view_create_by_idx ON metric_view (create_by)
type recording_rule (line 480) | CREATE TABLE recording_rule (
type recording_rule_group_id_idx (line 498) | CREATE INDEX recording_rule_group_id_idx ON recording_rule (group_id)
type recording_rule_update_at_idx (line 499) | CREATE INDEX recording_rule_update_at_idx ON recording_rule (update_at)
type alert_aggr_view (line 511) | CREATE TABLE alert_aggr_view (
type alert_aggr_view_create_by_idx (line 521) | CREATE INDEX alert_aggr_view_create_by_idx ON alert_aggr_view (create_by)
type alert_cur_event (line 529) | CREATE TABLE alert_cur_event (
type alert_cur_event_hash_idx (line 563) | CREATE INDEX alert_cur_event_hash_idx ON alert_cur_event (hash)
type alert_cur_event_rule_id_idx (line 564) | CREATE INDEX alert_cur_event_rule_id_idx ON alert_cur_event (rule_id)
type alert_cur_event_tg_idx (line 565) | CREATE INDEX alert_cur_event_tg_idx ON alert_cur_event (trigger_time, gr...
type alert_cur_event_nrn_idx (line 566) | CREATE INDEX alert_cur_event_nrn_idx ON alert_cur_event (notify_repeat_n...
type alert_his_event (line 589) | CREATE TABLE alert_his_event (
type alert_his_event_hash_idx (line 625) | CREATE INDEX alert_his_event_hash_idx ON alert_his_event (hash)
type alert_his_event_rule_id_idx (line 626) | CREATE INDEX alert_his_event_rule_id_idx ON alert_his_event (rule_id)
type alert_his_event_tg_idx (line 627) | CREATE INDEX alert_his_event_tg_idx ON alert_his_event (trigger_time, gr...
type alert_his_event_nrn_idx (line 628) | CREATE INDEX alert_his_event_nrn_idx ON alert_his_event (last_eval_time)
type task_tpl (line 649) | CREATE TABLE task_tpl
type task_tpl_group_id_idx (line 668) | CREATE INDEX task_tpl_group_id_idx ON task_tpl (group_id)
type task_tpl_host (line 673) | CREATE TABLE task_tpl_host
type task_tpl_host_id_host_idx (line 680) | CREATE INDEX task_tpl_host_id_host_idx ON task_tpl_host (id, host)
type task_record (line 685) | CREATE TABLE task_record
type task_record_cg_idx (line 705) | CREATE INDEX task_record_cg_idx ON task_record (create_at, group_id)
type task_record_create_by_idx (line 706) | CREATE INDEX task_record_create_by_idx ON task_record (create_by)
type task_record_event_id_idx (line 707) | CREATE INDEX task_record_event_id_idx ON task_record (event_id)
type alerting_engines (line 712) | CREATE TABLE alerting_engines
type datasource (line 726) | CREATE TABLE datasource
type builtin_cate (line 751) | CREATE TABLE builtin_cate (
type notify_tpl (line 758) | CREATE TABLE notify_tpl (
type sso_config (line 771) | CREATE TABLE sso_config (
type es_index_pattern (line 781) | CREATE TABLE es_index_pattern (
type builtin_metrics (line 800) | CREATE TABLE builtin_metrics (
type idx_collector (line 821) | CREATE INDEX idx_collector ON builtin_metrics (collector)
type idx_typ (line 822) | CREATE INDEX idx_typ ON builtin_metrics (typ)
type idx_name (line 823) | CREATE INDEX idx_name ON builtin_metrics (name)
type idx_lang (line 824) | CREATE INDEX idx_lang ON builtin_metrics (lang)
type metric_filter (line 843) | CREATE TABLE metric_filter (
type idx_metric_filter_name (line 854) | CREATE INDEX idx_metric_filter_name ON metric_filter (name)
type board_busigroup (line 856) | CREATE TABLE board_busigroup (
type builtin_components (line 863) | CREATE TABLE builtin_components (
type idx_ident (line 875) | CREATE INDEX idx_ident ON builtin_components (ident)
type builtin_payloads (line 877) | CREATE TABLE builtin_payloads (
type idx_component (line 893) | CREATE INDEX idx_component ON builtin_payloads (component)
type idx_builtin_payloads_name (line 894) | CREATE INDEX idx_builtin_payloads_name ON builtin_payloads (name)
type idx_cate (line 895) | CREATE INDEX idx_cate ON builtin_payloads (cate)
type idx_type (line 896) | CREATE INDEX idx_type ON builtin_payloads (type)
type dash_annotation (line 899) | CREATE TABLE dash_annotation (
type source_token (line 914) | CREATE TABLE source_token (
type idx_source_token_type_id_token (line 924) | CREATE INDEX idx_source_token_type_id_token ON source_token (source_type...
type notification_record (line 926) | CREATE TABLE notification_record (
type idx_evt (line 938) | CREATE INDEX idx_evt ON notification_record (event_id)
type target_busi_group (line 948) | CREATE TABLE target_busi_group (
type idx_target_group (line 955) | CREATE UNIQUE INDEX idx_target_group ON target_busi_group (target_ident,...
type user_token (line 957) | CREATE TABLE user_token (
type notify_rule (line 966) | CREATE TABLE notify_rule (
type notify_channel (line 980) | CREATE TABLE notify_channel (
type message_template (line 996) | CREATE TABLE message_template (
type event_pipeline (line 1011) | CREATE TABLE event_pipeline (
type embedded_product (line 1026) | CREATE TABLE embedded_product (
FILE: docker/compose-postgres/initsql_for_postgres/b-ibex-for-Postgres.sql
type task_meta (line 1) | CREATE TABLE task_meta
type task_meta_creator_idx (line 17) | CREATE INDEX task_meta_creator_idx ON task_meta (creator)
type task_meta_created_idx (line 18) | CREATE INDEX task_meta_created_idx ON task_meta (created)
type task_action (line 21) | CREATE TABLE task_action
type task_scheduler (line 29) | CREATE TABLE task_scheduler
type task_scheduler_id_scheduler_idx (line 34) | CREATE INDEX task_scheduler_id_scheduler_idx ON task_scheduler (id, sche...
type task_scheduler_health (line 37) | CREATE TABLE task_scheduler_health
type task_scheduler_health_clock_idx (line 43) | CREATE INDEX task_scheduler_health_clock_idx ON task_scheduler_health (c...
type task_host_doing (line 46) | CREATE TABLE task_host_doing
type task_host_doing_id_idx (line 53) | CREATE INDEX task_host_doing_id_idx ON task_host_doing (id)
type task_host_doing_host_idx (line 54) | CREATE INDEX task_host_doing_host_idx ON task_host_doing (host)
type task_host_0 (line 57) | CREATE TABLE task_host_0
type task_host_1 (line 69) | CREATE TABLE task_host_1
type task_host_2 (line 81) | CREATE TABLE task_host_2
type task_host_3 (line 93) | CREATE TABLE task_host_3
type task_host_4 (line 105) | CREATE TABLE task_host_4
type task_host_5 (line 117) | CREATE TABLE task_host_5
type task_host_6 (line 129) | CREATE TABLE task_host_6
type task_host_7 (line 141) | CREATE TABLE task_host_7
type task_host_8 (line 153) | CREATE TABLE task_host_8
type task_host_9 (line 165) | CREATE TABLE task_host_9
type task_host_10 (line 177) | CREATE TABLE task_host_10
type task_host_11 (line 189) | CREATE TABLE task_host_11
type task_host_12 (line 201) | CREATE TABLE task_host_12
type task_host_13 (line 213) | CREATE TABLE task_host_13
type task_host_14 (line 225) | CREATE TABLE task_host_14
type task_host_15 (line 237) | CREATE TABLE task_host_15
type task_host_16 (line 249) | CREATE TABLE task_host_16
type task_host_17 (line 261) | CREATE TABLE task_host_17
type task_host_18 (line 273) | CREATE TABLE task_host_18
type task_host_19 (line 285) | CREATE TABLE task_host_19
type task_host_20 (line 297) | CREATE TABLE task_host_20
type task_host_21 (line 309) | CREATE TABLE task_host_21
type task_host_22 (line 321) | CREATE TABLE task_host_22
type task_host_23 (line 333) | CREATE TABLE task_host_23
type task_host_24 (line 345) | CREATE TABLE task_host_24
type task_host_25 (line 357) | CREATE TABLE task_host_25
type task_host_26 (line 369) | CREATE TABLE task_host_26
type task_host_27 (line 381) | CREATE TABLE task_host_27
type task_host_28 (line 393) | CREATE TABLE task_host_28
type task_host_29 (line 405) | CREATE TABLE task_host_29
type task_host_30 (line 417) | CREATE TABLE task_host_30
type task_host_31 (line 429) | CREATE TABLE task_host_31
type task_host_32 (line 441) | CREATE TABLE task_host_32
type task_host_33 (line 453) | CREATE TABLE task_host_33
type task_host_34 (line 465) | CREATE TABLE task_host_34
type task_host_35 (line 477) | CREATE TABLE task_host_35
type task_host_36 (line 489) | CREATE TABLE task_host_36
type task_host_37 (line 501) | CREATE TABLE task_host_37
type task_host_38 (line 513) | CREATE TABLE task_host_38
type task_host_39 (line 525) | CREATE TABLE task_host_39
type task_host_40 (line 537) | CREATE TABLE task_host_40
type task_host_41 (line 549) | CREATE TABLE task_host_41
type task_host_42 (line 561) | CREATE TABLE task_host_42
type task_host_43 (line 573) | CREATE TABLE task_host_43
type task_host_44 (line 585) | CREATE TABLE task_host_44
type task_host_45 (line 597) | CREATE TABLE task_host_45
type task_host_46 (line 609) | CREATE TABLE task_host_46
type task_host_47 (line 621) | CREATE TABLE task_host_47
type task_host_48 (line 633) | CREATE TABLE task_host_48
type task_host_49 (line 645) | CREATE TABLE task_host_49
type task_host_50 (line 657) | CREATE TABLE task_host_50
type task_host_51 (line 669) | CREATE TABLE task_host_51
type task_host_52 (line 681) | CREATE TABLE task_host_52
type task_host_53 (line 693) | CREATE TABLE task_host_53
type task_host_54 (line 705) | CREATE TABLE task_host_54
type task_host_55 (line 717) | CREATE TABLE task_host_55
type task_host_56 (line 729) | CREATE TABLE task_host_56
type task_host_57 (line 741) | CREATE TABLE task_host_57
type task_host_58 (line 753) | CREATE TABLE task_host_58
type task_host_59 (line 765) | CREATE TABLE task_host_59
type task_host_60 (line 777) | CREATE TABLE task_host_60
type task_host_61 (line 789) | CREATE TABLE task_host_61
type task_host_62 (line 801) | CREATE TABLE task_host_62
type task_host_63 (line 813) | CREATE TABLE task_host_63
type task_host_64 (line 825) | CREATE TABLE task_host_64
type task_host_65 (line 837) | CREATE TABLE task_host_65
type task_host_66 (line 849) | CREATE TABLE task_host_66
type task_host_67 (line 861) | CREATE TABLE task_host_67
type task_host_68 (line 873) | CREATE TABLE task_host_68
type task_host_69 (line 885) | CREATE TABLE task_host_69
type task_host_70 (line 897) | CREATE TABLE task_host_70
type task_host_71 (line 909) | CREATE TABLE task_host_71
type task_host_72 (line 921) | CREATE TABLE task_host_72
type task_host_73 (line 933) | CREATE TABLE task_host_73
type task_host_74 (line 945) | CREATE TABLE task_host_74
type task_host_75 (line 957) | CREATE TABLE task_host_75
type task_host_76 (line 969) | CREATE TABLE task_host_76
type task_host_77 (line 981) | CREATE TABLE task_host_77
type task_host_78 (line 993) | CREATE TABLE task_host_78
type task_host_79 (line 1005) | CREATE TABLE task_host_79
type task_host_80 (line 1017) | CREATE TABLE task_host_80
type task_host_81 (line 1029) | CREATE TABLE task_host_81
type task_host_82 (line 1041) | CREATE TABLE task_host_82
type task_host_83 (line 1053) | CREATE TABLE task_host_83
type task_host_84 (line 1065) | CREATE TABLE task_host_84
type task_host_85 (line 1077) | CREATE TABLE task_host_85
type task_host_86 (line 1089) | CREATE TABLE task_host_86
type task_host_87 (line 1101) | CREATE TABLE task_host_87
type task_host_88 (line 1113) | CREATE TABLE task_host_88
type task_host_89 (line 1125) | CREATE TABLE task_host_89
type task_host_90 (line 1137) | CREATE TABLE task_host_90
type task_host_91 (line 1149) | CREATE TABLE task_host_91
type task_host_92 (line 1161) | CREATE TABLE task_host_92
type task_host_93 (line 1173) | CREATE TABLE task_host_93
type task_host_94 (line 1185) | CREATE TABLE task_host_94
type task_host_95 (line 1197) | CREATE TABLE task_host_95
type task_host_96 (line 1209) | CREATE TABLE task_host_96
type task_host_97 (line 1221) | CREATE TABLE task_host_97
type task_host_98 (line 1233) | CREATE TABLE task_host_98
type task_host_99 (line 1245) | CREATE TABLE task_host_99
FILE: docker/initsql/a-n9e.sql
type `users` (line 7) | CREATE TABLE `users` (
type `user_group` (line 30) | CREATE TABLE `user_group` (
type `user_group_member` (line 45) | CREATE TABLE `user_group_member` (
type `configs` (line 56) | CREATE TABLE `configs` (
type `role` (line 70) | CREATE TABLE `role` (
type `role_operation` (line 82) | CREATE TABLE `role_operation`(
type `busi_group` (line 154) | CREATE TABLE `busi_group` (
type `busi_group_member` (line 169) | CREATE TABLE `busi_group_member` (
type `board` (line 182) | CREATE TABLE `board` (
type `board_payload` (line 203) | CREATE TABLE `board_payload` (
type `dashboard` (line 210) | CREATE TABLE `dashboard` (
type `chart_group` (line 226) | CREATE TABLE `chart_group` (
type `chart` (line 236) | CREATE TABLE `chart` (
type `chart_share` (line 245) | CREATE TABLE `chart_share` (
type `alert_rule` (line 256) | CREATE TABLE `alert_rule` (
type `alert_mute` (line 303) | CREATE TABLE `alert_mute` (
type `alert_subscribe` (line 328) | CREATE TABLE `alert_subscribe` (
type `target` (line 363) | CREATE TABLE `target` (
type `metric_view` (line 385) | CREATE TABLE `metric_view` (
type `recording_rule` (line 399) | CREATE TABLE `recording_rule` (
type `alert_aggr_view` (line 422) | CREATE TABLE `alert_aggr_view` (
type `alert_cur_event` (line 437) | CREATE TABLE `alert_cur_event` (
type `alert_his_event` (line 478) | CREATE TABLE `alert_his_event` (
type `board_busigroup` (line 521) | CREATE TABLE `board_busigroup` (
type `builtin_components` (line 527) | CREATE TABLE `builtin_components` (
type `builtin_payloads` (line 541) | CREATE TABLE `builtin_payloads` (
type notification_record (line 564) | CREATE TABLE notification_record (
type `task_tpl` (line 577) | CREATE TABLE `task_tpl`
type `task_tpl_host` (line 598) | CREATE TABLE `task_tpl_host`
type `task_record` (line 607) | CREATE TABLE `task_record`
type `alerting_engines` (line 631) | CREATE TABLE `alerting_engines`
type `datasource` (line 643) | CREATE TABLE `datasource`
type `builtin_cate` (line 668) | CREATE TABLE `builtin_cate` (
type `notify_tpl` (line 675) | CREATE TABLE `notify_tpl` (
type `sso_config` (line 688) | CREATE TABLE `sso_config` (
type `es_index_pattern` (line 697) | CREATE TABLE `es_index_pattern` (
type `builtin_metrics` (line 715) | CREATE TABLE `builtin_metrics` (
type `metric_filter` (line 740) | CREATE TABLE `metric_filter` (
type `target_busi_group` (line 753) | CREATE TABLE `target_busi_group` (
type `dash_annotation` (line 763) | CREATE TABLE `dash_annotation` (
type `user_token` (line 780) | CREATE TABLE `user_token` (
type `notify_rule` (line 791) | CREATE TABLE `notify_rule` (
type `notify_channel` (line 806) | CREATE TABLE `notify_channel` (
type `message_template` (line 823) | CREATE TABLE `message_template` (
type `event_pipeline` (line 839) | CREATE TABLE `event_pipeline` (
type `embedded_product` (line 855) | CREATE TABLE `embedded_product` (
type `task_meta` (line 868) | CREATE TABLE `task_meta`
type `task_action` (line 889) | CREATE TABLE `task_action`
type `task_scheduler` (line 898) | CREATE TABLE `task_scheduler`
type `task_scheduler_health` (line 906) | CREATE TABLE `task_scheduler_health`
type `task_host_doing` (line 915) | CREATE TABLE `task_host_doing`
type task_host_0 (line 926) | CREATE TABLE task_host_0
type task_host_1 (line 939) | CREATE TABLE task_host_1
type task_host_2 (line 952) | CREATE TABLE task_host_2
type task_host_3 (line 965) | CREATE TABLE task_host_3
type task_host_4 (line 978) | CREATE TABLE task_host_4
type task_host_5 (line 991) | CREATE TABLE task_host_5
type task_host_6 (line 1004) | CREATE TABLE task_host_6
type task_host_7 (line 1017) | CREATE TABLE task_host_7
type task_host_8 (line 1030) | CREATE TABLE task_host_8
type task_host_9 (line 1043) | CREATE TABLE task_host_9
type task_host_10 (line 1056) | CREATE TABLE task_host_10
type task_host_11 (line 1069) | CREATE TABLE task_host_11
type task_host_12 (line 1082) | CREATE TABLE task_host_12
type task_host_13 (line 1095) | CREATE TABLE task_host_13
type task_host_14 (line 1108) | CREATE TABLE task_host_14
type task_host_15 (line 1121) | CREATE TABLE task_host_15
type task_host_16 (line 1134) | CREATE TABLE task_host_16
type task_host_17 (line 1147) | CREATE TABLE task_host_17
type task_host_18 (line 1160) | CREATE TABLE task_host_18
type task_host_19 (line 1173) | CREATE TABLE task_host_19
type task_host_20 (line 1186) | CREATE TABLE task_host_20
type task_host_21 (line 1199) | CREATE TABLE task_host_21
type task_host_22 (line 1212) | CREATE TABLE task_host_22
type task_host_23 (line 1225) | CREATE TABLE task_host_23
type task_host_24 (line 1238) | CREATE TABLE task_host_24
type task_host_25 (line 1251) | CREATE TABLE task_host_25
type task_host_26 (line 1264) | CREATE TABLE task_host_26
type task_host_27 (line 1277) | CREATE TABLE task_host_27
type task_host_28 (line 1290) | CREATE TABLE task_host_28
type task_host_29 (line 1303) | CREATE TABLE task_host_29
type task_host_30 (line 1316) | CREATE TABLE task_host_30
type task_host_31 (line 1329) | CREATE TABLE task_host_31
type task_host_32 (line 1342) | CREATE TABLE task_host_32
type task_host_33 (line 1355) | CREATE TABLE task_host_33
type task_host_34 (line 1368) | CREATE TABLE task_host_34
type task_host_35 (line 1381) | CREATE TABLE task_host_35
type task_host_36 (line 1394) | CREATE TABLE task_host_36
type task_host_37 (line 1407) | CREATE TABLE task_host_37
type task_host_38 (line 1420) | CREATE TABLE task_host_38
type task_host_39 (line 1433) | CREATE TABLE task_host_39
type task_host_40 (line 1446) | CREATE TABLE task_host_40
type task_host_41 (line 1459) | CREATE TABLE task_host_41
type task_host_42 (line 1472) | CREATE TABLE task_host_42
type task_host_43 (line 1485) | CREATE TABLE task_host_43
type task_host_44 (line 1498) | CREATE TABLE task_host_44
type task_host_45 (line 1511) | CREATE TABLE task_host_45
type task_host_46 (line 1524) | CREATE TABLE task_host_46
type task_host_47 (line 1537) | CREATE TABLE task_host_47
type task_host_48 (line 1550) | CREATE TABLE task_host_48
type task_host_49 (line 1563) | CREATE TABLE task_host_49
type task_host_50 (line 1576) | CREATE TABLE task_host_50
type task_host_51 (line 1589) | CREATE TABLE task_host_51
type task_host_52 (line 1602) | CREATE TABLE task_host_52
type task_host_53 (line 1615) | CREATE TABLE task_host_53
type task_host_54 (line 1628) | CREATE TABLE task_host_54
type task_host_55 (line 1641) | CREATE TABLE task_host_55
type task_host_56 (line 1654) | CREATE TABLE task_host_56
type task_host_57 (line 1667) | CREATE TABLE task_host_57
type task_host_58 (line 1680) | CREATE TABLE task_host_58
type task_host_59 (line 1693) | CREATE TABLE task_host_59
type task_host_60 (line 1706) | CREATE TABLE task_host_60
type task_host_61 (line 1719) | CREATE TABLE task_host_61
type task_host_62 (line 1732) | CREATE TABLE task_host_62
type task_host_63 (line 1745) | CREATE TABLE task_host_63
type task_host_64 (line 1758) | CREATE TABLE task_host_64
type task_host_65 (line 1771) | CREATE TABLE task_host_65
type task_host_66 (line 1784) | CREATE TABLE task_host_66
type task_host_67 (line 1797) | CREATE TABLE task_host_67
type task_host_68 (line 1810) | CREATE TABLE task_host_68
type task_host_69 (line 1823) | CREATE TABLE task_host_69
type task_host_70 (line 1836) | CREATE TABLE task_host_70
type task_host_71 (line 1849) | CREATE TABLE task_host_71
type task_host_72 (line 1862) | CREATE TABLE task_host_72
type task_host_73 (line 1875) | CREATE TABLE task_host_73
type task_host_74 (line 1888) | CREATE TABLE task_host_74
type task_host_75 (line 1901) | CREATE TABLE task_host_75
type task_host_76 (line 1914) | CREATE TABLE task_host_76
type task_host_77 (line 1927) | CREATE TABLE task_host_77
type task_host_78 (line 1940) | CREATE TABLE task_host_78
type task_host_79 (line 1953) | CREATE TABLE task_host_79
type task_host_80 (line 1966) | CREATE TABLE task_host_80
type task_host_81 (line 1979) | CREATE TABLE task_host_81
type task_host_82 (line 1992) | CREATE TABLE task_host_82
type task_host_83 (line 2005) | CREATE TABLE task_host_83
type task_host_84 (line 2018) | CREATE TABLE task_host_84
type task_host_85 (line 2031) | CREATE TABLE task_host_85
type task_host_86 (line 2044) | CREATE TABLE task_host_86
type task_host_87 (line 2057) | CREATE TABLE task_host_87
type task_host_88 (line 2070) | CREATE TABLE task_host_88
type task_host_89 (line 2083) | CREATE TABLE task_host_89
type task_host_90 (line 2096) | CREATE TABLE task_host_90
type task_host_91 (line 2109) | CREATE TABLE task_host_91
type task_host_92 (line 2122) | CREATE TABLE task_host_92
type task_host_93 (line 2135) | CREATE TABLE task_host_93
type task_host_94 (line 2148) | CREATE TABLE task_host_94
type task_host_95 (line 2161) | CREATE TABLE task_host_95
type task_host_96 (line 2174) | CREATE TABLE task_host_96
type task_host_97 (line 2187) | CREATE TABLE task_host_97
type task_host_98 (line 2200) | CREATE TABLE task_host_98
type task_host_99 (line 2213) | CREATE TABLE task_host_99
type `source_token` (line 2226) | CREATE TABLE `source_token` (
FILE: docker/migratesql/migrate.sql
type `builtin_metrics` (line 2) | CREATE TABLE `builtin_metrics` (
type `metric_filter` (line 22) | CREATE TABLE `metric_filter` (
type `board_busigroup` (line 36) | CREATE TABLE `board_busigroup` (
type `builtin_components` (line 43) | CREATE TABLE `builtin_components` (
type `builtin_payloads` (line 56) | CREATE TABLE `builtin_payloads` (
type notification_record (line 91) | CREATE TABLE notification_record (
type `target_busi_group` (line 111) | CREATE TABLE `target_busi_group` (
type `dash_annotation` (line 138) | CREATE TABLE `dash_annotation` (
type `user_token` (line 156) | CREATE TABLE `user_token` (
type `notify_rule` (line 167) | CREATE TABLE `notify_rule` (
type `notify_channel` (line 181) | CREATE TABLE `notify_channel` (
type `message_template` (line 197) | CREATE TABLE `message_template` (
type `event_pipeline` (line 232) | CREATE TABLE `event_pipeline` (
type `embedded_product` (line 248) | CREATE TABLE `embedded_product` (
type `source_token` (line 262) | CREATE TABLE `source_token` (
type `event_pipeline_execution` (line 307) | CREATE TABLE `event_pipeline_execution` (
type `saved_view` (line 336) | CREATE TABLE `saved_view` (
type `user_view_favorite` (line 352) | CREATE TABLE `user_view_favorite` (
FILE: docker/sqlite.sql
type `users` (line 1) | CREATE TABLE `users` (
type idx_users_username (line 20) | CREATE UNIQUE INDEX idx_users_username ON `users` (username)
type `user_group` (line 24) | CREATE TABLE `user_group` (
type `idx_user_group_create_by` (line 33) | CREATE INDEX `idx_user_group_create_by` ON `user_group` (`create_by` asc)
type `idx_user_group_update_at` (line 34) | CREATE INDEX `idx_user_group_update_at` ON `user_group` (`update_at` asc)
type `user_group_member` (line 38) | CREATE TABLE `user_group_member` (
type `idx_user_group_member_group_id` (line 43) | CREATE INDEX `idx_user_group_member_group_id` ON `user_group_member` (`g...
type `idx_user_group_member_user_id` (line 44) | CREATE INDEX `idx_user_group_member_user_id` ON `user_group_member` (`us...
type `configs` (line 48) | CREATE TABLE `configs` (
type `role` (line 61) | CREATE TABLE `role` (
type `role_operation` (line 72) | CREATE TABLE `role_operation`(
type `idx_role_operation_role_name` (line 77) | CREATE INDEX `idx_role_operation_role_name` ON `role_operation` (`role_n...
type `idx_role_operation_operation` (line 78) | CREATE INDEX `idx_role_operation_operation` ON `role_operation` (`operat...
type `busi_group` (line 149) | CREATE TABLE `busi_group` (
type `busi_group_member` (line 162) | CREATE TABLE `busi_group_member` (
type `idx_busi_group_member_busi_group_id` (line 168) | CREATE INDEX `idx_busi_group_member_busi_group_id` ON `busi_group_member...
type `idx_busi_group_member_user_group_id` (line 169) | CREATE INDEX `idx_busi_group_member_user_group_id` ON `busi_group_member...
type `board` (line 174) | CREATE TABLE `board` (
type idx_board_group_id_name (line 190) | CREATE UNIQUE INDEX idx_board_group_id_name ON `board` (group_id, name)
type `idx_board_ident` (line 191) | CREATE INDEX `idx_board_ident` ON `board` (`ident` asc)
type `board_payload` (line 194) | CREATE TABLE `board_payload` (
type `chart` (line 199) | CREATE TABLE `chart` (
type idx_chart_group_id (line 206) | CREATE INDEX idx_chart_group_id ON `chart` (group_id)
type `chart_share` (line 208) | CREATE TABLE `chart_share` (
type `idx_chart_share_create_at` (line 216) | CREATE INDEX `idx_chart_share_create_at` ON `chart_share` (`create_at` asc)
type `alert_rule` (line 218) | CREATE TABLE `alert_rule` (
type `idx_alert_rule_group_id` (line 259) | CREATE INDEX `idx_alert_rule_group_id` ON `alert_rule` (`group_id` asc)
type `idx_alert_rule_update_at` (line 260) | CREATE INDEX `idx_alert_rule_update_at` ON `alert_rule` (`update_at` asc)
type `alert_mute` (line 262) | CREATE TABLE `alert_mute` (
type `idx_alert_mute_create_at` (line 283) | CREATE INDEX `idx_alert_mute_create_at` ON `alert_mute` (`create_at` asc)
type `idx_alert_mute_group_id` (line 284) | CREATE INDEX `idx_alert_mute_group_id` ON `alert_mute` (`group_id` asc)
type `alert_subscribe` (line 286) | CREATE TABLE `alert_subscribe` (
type `idx_alert_subscribe_update_at` (line 315) | CREATE INDEX `idx_alert_subscribe_update_at` ON `alert_subscribe` (`upda...
type `idx_alert_subscribe_group_id` (line 316) | CREATE INDEX `idx_alert_subscribe_group_id` ON `alert_subscribe` (`group...
type `target` (line 319) | CREATE TABLE `target` (
type `idx_target_group_id` (line 333) | CREATE INDEX `idx_target_group_id` ON `target` (`group_id` asc)
type idx_target_ident (line 334) | CREATE UNIQUE INDEX idx_target_ident ON `target` (ident)
type idx_host_ip (line 335) | CREATE INDEX idx_host_ip ON `target` (host_ip)
type idx_agent_version (line 336) | CREATE INDEX idx_agent_version ON `target` (agent_version)
type idx_engine_name (line 337) | CREATE INDEX idx_engine_name ON `target` (engine_name)
type idx_os (line 338) | CREATE INDEX idx_os ON `target` (os)
type `metric_view` (line 340) | CREATE TABLE `metric_view` (
type `idx_metric_view_create_by` (line 349) | CREATE INDEX `idx_metric_view_create_by` ON `metric_view` (`create_by` asc)
type `recording_rule` (line 353) | CREATE TABLE `recording_rule` (
type `idx_recording_rule_group_id` (line 372) | CREATE INDEX `idx_recording_rule_group_id` ON `recording_rule` (`group_i...
type `idx_recording_rule_update_at` (line 373) | CREATE INDEX `idx_recording_rule_update_at` ON `recording_rule` (`update...
type `alert_aggr_view` (line 375) | CREATE TABLE `alert_aggr_view` (
type `idx_alert_aggr_view_create_by` (line 384) | CREATE INDEX `idx_alert_aggr_view_create_by` ON `alert_aggr_view` (`crea...
type `alert_cur_event` (line 389) | CREATE TABLE `alert_cur_event` (
type `idx_alert_cur_event_hash` (line 422) | CREATE INDEX `idx_alert_cur_event_hash` ON `alert_cur_event` (`hash` asc)
type `idx_alert_cur_event_rule_id` (line 423) | CREATE INDEX `idx_alert_cur_event_rule_id` ON `alert_cur_event` (`rule_i...
type `idx_alert_cur_event_trigger_time_group_id` (line 424) | CREATE INDEX `idx_alert_cur_event_trigger_time_group_id` ON `alert_cur_e...
type `idx_alert_cur_event_notify_repeat_next` (line 425) | CREATE INDEX `idx_alert_cur_event_notify_repeat_next` ON `alert_cur_even...
type `alert_his_event` (line 427) | CREATE TABLE `alert_his_event` (
type `idx_alert_his_event_last_eval_time` (line 463) | CREATE INDEX `idx_alert_his_event_last_eval_time` ON `alert_his_event` (...
type `idx_alert_his_event_hash` (line 464) | CREATE INDEX `idx_alert_his_event_hash` ON `alert_his_event` (`hash` asc)
type `idx_alert_his_event_rule_id` (line 465) | CREATE INDEX `idx_alert_his_event_rule_id` ON `alert_his_event` (`rule_i...
type `idx_alert_his_event_trigger_time_group_id` (line 466) | CREATE INDEX `idx_alert_his_event_trigger_time_group_id` ON `alert_his_e...
type `board_busigroup` (line 468) | CREATE TABLE `board_busigroup` (
type `builtin_components` (line 474) | CREATE TABLE `builtin_components` (
type `idx_builtin_components_ident` (line 484) | CREATE INDEX `idx_builtin_components_ident` ON `builtin_components` (`id...
type `builtin_payloads` (line 486) | CREATE TABLE `builtin_payloads` (
type `idx_builtin_payloads_component` (line 502) | CREATE INDEX `idx_builtin_payloads_component` ON `builtin_payloads` (`co...
type `idx_builtin_payloads_name` (line 503) | CREATE INDEX `idx_builtin_payloads_name` ON `builtin_payloads` (`name` asc)
type `idx_builtin_payloads_cate` (line 504) | CREATE INDEX `idx_builtin_payloads_cate` ON `builtin_payloads` (`cate` asc)
type `idx_builtin_payloads_type` (line 505) | CREATE INDEX `idx_builtin_payloads_type` ON `builtin_payloads` (`type` asc)
type idx_uuid (line 506) | CREATE INDEX idx_uuid ON `builtin_payloads` (uuid)
type `notification_record` (line 509) | CREATE TABLE `notification_record` (
type idx_evt (line 519) | CREATE INDEX idx_evt ON notification_record (event_id)
type `task_tpl` (line 521) | CREATE TABLE `task_tpl` (
type `idx_task_tpl_group_id` (line 538) | CREATE INDEX `idx_task_tpl_group_id` ON `task_tpl` (`group_id` asc)
type `task_tpl_host` (line 540) | CREATE TABLE `task_tpl_host` (
type `idx_task_tpl_host_id_host` (line 545) | CREATE INDEX `idx_task_tpl_host_id_host` ON `task_tpl_host` (`id`, `host...
type `task_record` (line 547) | CREATE TABLE `task_record` (
type `idx_task_record_create_at_group_id` (line 565) | CREATE INDEX `idx_task_record_create_at_group_id` ON `task_record` (`cre...
type `idx_task_record_create_by` (line 566) | CREATE INDEX `idx_task_record_create_by` ON `task_record` (`create_by` asc)
type `idx_task_record_event_id` (line 567) | CREATE INDEX `idx_task_record_event_id` ON `task_record` (`event_id` asc)
type `alerting_engines` (line 570) | CREATE TABLE `alerting_engines` (
type `datasource` (line 578) | CREATE TABLE `datasource`
type idx_datasource_name (line 600) | CREATE UNIQUE INDEX idx_datasource_name ON datasource (name)
type `builtin_cate` (line 602) | CREATE TABLE `builtin_cate` (
type `notify_tpl` (line 608) | CREATE TABLE `notify_tpl` (
type idx_notify_tpl_channel (line 619) | CREATE UNIQUE INDEX idx_notify_tpl_channel ON notify_tpl (channel)
type `sso_config` (line 621) | CREATE TABLE `sso_config` (
type idx_sso_config_name (line 628) | CREATE UNIQUE INDEX idx_sso_config_name ON sso_config (name)
type `es_index_pattern` (line 630) | CREATE TABLE `es_index_pattern` (
type idx_es_index_pattern_datasource_id_name (line 645) | CREATE UNIQUE INDEX idx_es_index_pattern_datasource_id_name ON es_index_...
type `builtin_metrics` (line 647) | CREATE TABLE `builtin_metrics` (
type idx_collector (line 666) | CREATE INDEX idx_collector ON builtin_metrics (collector)
type idx_typ (line 667) | CREATE INDEX idx_typ ON builtin_metrics (typ)
type idx_builtinmetric_name (line 668) | CREATE INDEX idx_builtinmetric_name ON builtin_metrics (name)
type idx_lang (line 669) | CREATE INDEX idx_lang ON builtin_metrics (lang)
type `metric_filter` (line 672) | CREATE TABLE `metric_filter` (
type `idx_metric_filter_name` (line 682) | CREATE INDEX `idx_metric_filter_name` ON `metric_filter` (`name` asc)
type `target_busi_group` (line 684) | CREATE TABLE `target_busi_group` (
type idx_target_busi_group (line 691) | CREATE UNIQUE INDEX idx_target_busi_group ON target_busi_group (target_i...
type `dash_annotation` (line 694) | CREATE TABLE `dash_annotation` (
type `task_meta` (line 709) | CREATE TABLE `task_meta`
type `idx_task_meta_creator` (line 724) | CREATE INDEX `idx_task_meta_creator` ON `task_meta` (`creator` asc)
type `idx_task_meta_created` (line 725) | CREATE INDEX `idx_task_meta_created` ON `task_meta` (`created` asc)
type `task_action` (line 729) | CREATE TABLE `task_action`
type `task_scheduler` (line 736) | CREATE TABLE `task_scheduler`
type `idx_task_scheduler_id_scheduler` (line 741) | CREATE INDEX `idx_task_scheduler_id_scheduler` ON `task_scheduler` (`id`...
type `task_scheduler_health` (line 743) | CREATE TABLE `task_scheduler_health`
type `idx_task_scheduler_health_clock` (line 748) | CREATE INDEX `idx_task_scheduler_health_clock` ON `task_scheduler_health...
type `task_host_doing` (line 750) | CREATE TABLE `task_host_doing`
type `idx_task_host_doing_id` (line 757) | CREATE INDEX `idx_task_host_doing_id` ON `task_host_doing` (`id` asc)
type `idx_task_host_doing_host` (line 758) | CREATE INDEX `idx_task_host_doing_host` ON `task_host_doing` (`host` asc)
type task_host_0 (line 760) | CREATE TABLE task_host_0
type task_host_1 (line 771) | CREATE TABLE task_host_1
type task_host_2 (line 782) | CREATE TABLE task_host_2
type task_host_3 (line 793) | CREATE TABLE task_host_3
type task_host_4 (line 804) | CREATE TABLE task_host_4
type task_host_5 (line 815) | CREATE TABLE task_host_5
type task_host_6 (line 826) | CREATE TABLE task_host_6
type task_host_7 (line 837) | CREATE TABLE task_host_7
type task_host_8 (line 848) | CREATE TABLE task_host_8
type task_host_9 (line 859) | CREATE TABLE task_host_9
type task_host_10 (line 870) | CREATE TABLE task_host_10
type task_host_11 (line 881) | CREATE TABLE task_host_11
type task_host_12 (line 892) | CREATE TABLE task_host_12
type task_host_13 (line 903) | CREATE TABLE task_host_13
type task_host_14 (line 914) | CREATE TABLE task_host_14
type task_host_15 (line 925) | CREATE TABLE task_host_15
type task_host_16 (line 936) | CREATE TABLE task_host_16
type task_host_17 (line 947) | CREATE TABLE task_host_17
type task_host_18 (line 958) | CREATE TABLE task_host_18
type task_host_19 (line 969) | CREATE TABLE task_host_19
type task_host_20 (line 980) | CREATE TABLE task_host_20
type task_host_21 (line 991) | CREATE TABLE task_host_21
type task_host_22 (line 1002) | CREATE TABLE task_host_22
type task_host_23 (line 1013) | CREATE TABLE task_host_23
type task_host_24 (line 1024) | CREATE TABLE task_host_24
type task_host_25 (line 1035) | CREATE TABLE task_host_25
type task_host_26 (line 1046) | CREATE TABLE task_host_26
type task_host_27 (line 1057) | CREATE TABLE task_host_27
type task_host_28 (line 1068) | CREATE TABLE task_host_28
type task_host_29 (line 1079) | CREATE TABLE task_host_29
type task_host_30 (line 1090) | CREATE TABLE task_host_30
type task_host_31 (line 1101) | CREATE TABLE task_host_31
type task_host_32 (line 1112) | CREATE TABLE task_host_32
type task_host_33 (line 1123) | CREATE TABLE task_host_33
type task_host_34 (line 1134) | CREATE TABLE task_host_34
type task_host_35 (line 1145) | CREATE TABLE task_host_35
type task_host_36 (line 1156) | CREATE TABLE task_host_36
type task_host_37 (line 1167) | CREATE TABLE task_host_37
type task_host_38 (line 1178) | CREATE TABLE task_host_38
type task_host_39 (line 1189) | CREATE TABLE task_host_39
type task_host_40 (line 1200) | CREATE TABLE task_host_40
type task_host_41 (line 1211) | CREATE TABLE task_host_41
type task_host_42 (line 1222) | CREATE TABLE task_host_42
type task_host_43 (line 1233) | CREATE TABLE task_host_43
type task_host_44 (line 1244) | CREATE TABLE task_host_44
type task_host_45 (line 1255) | CREATE TABLE task_host_45
type task_host_46 (line 1266) | CREATE TABLE task_host_46
type task_host_47 (line 1277) | CREATE TABLE task_host_47
type task_host_48 (line 1288) | CREATE TABLE task_host_48
type task_host_49 (line 1299) | CREATE TABLE task_host_49
type task_host_50 (line 1310) | CREATE TABLE task_host_50
type task_host_51 (line 1321) | CREATE TABLE task_host_51
type task_host_52 (line 1332) | CREATE TABLE task_host_52
type task_host_53 (line 1343) | CREATE TABLE task_host_53
type task_host_54 (line 1354) | CREATE TABLE task_host_54
type task_host_55 (line 1365) | CREATE TABLE task_host_55
type task_host_56 (line 1376) | CREATE TABLE task_host_56
type task_host_57 (line 1387) | CREATE TABLE task_host_57
type task_host_58 (line 1398) | CREATE TABLE task_host_58
type task_host_59 (line 1409) | CREATE TABLE task_host_59
type task_host_60 (line 1420) | CREATE TABLE task_host_60
type task_host_61 (line 1431) | CREATE TABLE task_host_61
type task_host_62 (line 1442) | CREATE TABLE task_host_62
type task_host_63 (line 1453) | CREATE TABLE task_host_63
type task_host_64 (line 1464) | CREATE TABLE task_host_64
type task_host_65 (line 1475) | CREATE TABLE task_host_65
type task_host_66 (line 1486) | CREATE TABLE task_host_66
type task_host_67 (line 1497) | CREATE TABLE task_host_67
type task_host_68 (line 1508) | CREATE TABLE task_host_68
type task_host_69 (line 1519) | CREATE TABLE task_host_69
type task_host_70 (line 1530) | CREATE TABLE task_host_70
type task_host_71 (line 1541) | CREATE TABLE task_host_71
type task_host_72 (line 1552) | CREATE TABLE task_host_72
type task_host_73 (line 1563) | CREATE TABLE task_host_73
type task_host_74 (line 1574) | CREATE TABLE task_host_74
type task_host_75 (line 1585) | CREATE TABLE task_host_75
type task_host_76 (line 1596) | CREATE TABLE task_host_76
type task_host_77 (line 1607) | CREATE TABLE task_host_77
type task_host_78 (line 1618) | CREATE TABLE task_host_78
type task_host_79 (line 1629) | CREATE TABLE task_host_79
type task_host_80 (line 1640) | CREATE TABLE task_host_80
type task_host_81 (line 1651) | CREATE TABLE task_host_81
type task_host_82 (line 1662) | CREATE TABLE task_host_82
type task_host_83 (line 1673) | CREATE TABLE task_host_83
type task_host_84 (line 1684) | CREATE TABLE task_host_84
type task_host_85 (line 1695) | CREATE TABLE task_host_85
type task_host_86 (line 1706) | CREATE TABLE task_host_86
type task_host_87 (line 1717) | CREATE TABLE task_host_87
type task_host_88 (line 1728) | CREATE TABLE task_host_88
type task_host_89 (line 1739) | CREATE TABLE task_host_89
type task_host_90 (line 1750) | CREATE TABLE task_host_90
type task_host_91 (line 1761) | CREATE TABLE task_host_91
type task_host_92 (line 1772) | CREATE TABLE task_host_92
type task_host_93 (line 1783) | CREATE TABLE task_host_93
type task_host_94 (line 1794) | CREATE TABLE task_host_94
type task_host_95 (line 1805) | CREATE TABLE task_host_95
type task_host_96 (line 1816) | CREATE TABLE task_host_96
type task_host_97 (line 1827) | CREATE TABLE task_host_97
type task_host_98 (line 1838) | CREATE TABLE task_host_98
type task_host_99 (line 1849) | CREATE TABLE task_host_99
FILE: dscache/cache.go
type Cache (line 10) | type Cache struct
method Put (line 20) | func (cs *Cache) Put(cate string, dsId int64, ds datasource.Datasource) {
method Get (line 47) | func (cs *Cache) Get(cate string, dsId int64) (datasource.Datasource, ...
method Delete (line 61) | func (cs *Cache) Delete(cate string, dsId int64) {
method GetAllIds (line 73) | func (cs *Cache) GetAllIds() map[string][]int64 {
FILE: dscache/sync.go
function Init (line 30) | func Init(ctx *ctx.Context, fromAPI bool) {
type ListInput (line 52) | type ListInput struct
type DSReply (line 60) | type DSReply struct
type DSReplyEncrypt (line 67) | type DSReplyEncrypt struct
function getDatasourcesFromDBLoop (line 74) | func getDatasourcesFromDBLoop(ctx *ctx.Context, fromAPI bool) {
function tdN9eToDatasourceInfo (line 141) | func tdN9eToDatasourceInfo(ds *datasource.DatasourceInfo, item models.Da...
function esN9eToDatasourceInfo (line 155) | func esN9eToDatasourceInfo(ds *datasource.DatasourceInfo, item models.Da...
function PutDatasources (line 176) | func PutDatasources(items []datasource.DatasourceInfo) {
FILE: dskit/clickhouse/clickhouse.go
constant ckDataSource (line 23) | ckDataSource = "clickhouse://%s:%s@%s?read_timeout=10s"
constant DefaultLimit (line 25) | DefaultLimit = 500
type Clickhouse (line 28) | type Clickhouse struct
method InitCli (line 47) | func (c *Clickhouse) InitCli() error {
method QueryRows (line 207) | func (c *Clickhouse) QueryRows(ctx context.Context, query string) (*sq...
method ShowDatabases (line 231) | func (c *Clickhouse) ShowDatabases(ctx context.Context) ([]string, err...
method ShowTables (line 251) | func (c *Clickhouse) ShowTables(ctx context.Context, database string) ...
method DescribeTable (line 272) | func (c *Clickhouse) DescribeTable(ctx context.Context, query interfac...
method ExecQueryBySqlDB (line 299) | func (c *Clickhouse) ExecQueryBySqlDB(ctx context.Context, sql string)...
method Query (line 340) | func (c *Clickhouse) Query(ctx context.Context, query interface{}) ([]...
method CheckMaxQueryRows (line 375) | func (c *Clickhouse) CheckMaxQueryRows(ctx context.Context, sql string...
constant ShowDatabases (line 202) | ShowDatabases = "SHOW DATABASES"
constant ShowTables (line 203) | ShowTables = "SELECT name FROM system.tables WHERE database = '%s'"
constant DescTable (line 204) | DescTable = "SELECT name,type FROM system.columns WHERE database='%s...
FILE: dskit/clickhouse/clickhouse_test.go
function Test_Timeseries (line 13) | func Test_Timeseries(t *testing.T) {
FILE: dskit/clickhouse/timeseries.go
constant TimeFieldFormatEpochMilli (line 12) | TimeFieldFormatEpochMilli = "epoch_millis"
constant TimeFieldFormatEpochSecond (line 13) | TimeFieldFormatEpochSecond = "epoch_second"
type QueryParam (line 17) | type QueryParam struct
method QueryTimeseries (line 46) | func (c *Clickhouse) QueryTimeseries(ctx context.Context, query *QueryPa...
FILE: dskit/doris/doris.go
constant ShowIndexFieldIndexType (line 22) | ShowIndexFieldIndexType = "index_type"
constant ShowIndexFieldColumnName (line 23) | ShowIndexFieldColumnName = "column_name"
constant ShowIndexKeyName (line 24) | ShowIndexKeyName = "key_name"
constant SQLShowIndex (line 26) | SQLShowIndex = "SHOW INDEX FROM "
type Doris (line 30) | type Doris struct
method NewConn (line 70) | func (d *Doris) NewConn(ctx context.Context, database string) (*sql.DB...
method NewWriteConn (line 130) | func (d *Doris) NewWriteConn(ctx context.Context, database string) (*s...
method createTimeoutContext (line 201) | func (d *Doris) createTimeoutContext(ctx context.Context) (context.Con...
method ShowDatabases (line 210) | func (d *Doris) ShowDatabases(ctx context.Context) ([]string, error) {
method ShowResources (line 237) | func (d *Doris) ShowResources(ctx context.Context, resourceType string...
method ShowTables (line 297) | func (d *Doris) ShowTables(ctx context.Context, database string) ([]st...
method DescTable (line 325) | func (d *Doris) DescTable(ctx context.Context, database, table string)...
method ShowIndexes (line 406) | func (d *Doris) ShowIndexes(ctx context.Context, database, table strin...
method SelectRows (line 482) | func (d *Doris) SelectRows(ctx context.Context, database, table, query...
method ExecQuery (line 498) | func (d *Doris) ExecQuery(ctx context.Context, database string, sql st...
method ExecContext (line 547) | func (d *Doris) ExecContext(ctx context.Context, database string, sql ...
method ExecBatchSQL (line 561) | func (d *Doris) ExecBatchSQL(ctx context.Context, database string, sql...
function NewDorisWithSettings (line 48) | func NewDorisWithSettings(ctx context.Context, settings interface{}) (*D...
type TableIndexInfo (line 399) | type TableIndexInfo struct
function SplitSQLStatements (line 594) | func SplitSQLStatements(sqlBatch string) []string {
function isWhitespace (line 707) | func isWhitespace(c byte) bool {
FILE: dskit/doris/logs.go
constant TimeseriesAggregationTimestamp (line 10) | TimeseriesAggregationTimestamp = "__ts__"
method QueryLogs (line 14) | func (d *Doris) QueryLogs(ctx context.Context, query *QueryParam) ([]map...
method QueryHistogram (line 20) | func (d *Doris) QueryHistogram(ctx context.Context, query *QueryParam) (...
FILE: dskit/doris/sql_analyzer.go
type SQLAnalyzeResult (line 24) | type SQLAnalyzeResult struct
function AnalyzeSQL (line 31) | func AnalyzeSQL(sql string) (*SQLAnalyzeResult, error) {
function analyzeSelectStmt (line 62) | func analyzeSelectStmt(sel *ast.SelectStmt, result *SQLAnalyzeResult) {
function selectHasAggregate (line 92) | func selectHasAggregate(node ast.Node) bool {
function analyzeSetOprStmt (line 121) | func analyzeSetOprStmt(setOpr *ast.SetOprStmt, result *SQLAnalyzeResult) {
function hasAggregateFunc (line 156) | func hasAggregateFunc(expr ast.ExprNode) bool {
type aggregateChecker (line 163) | type aggregateChecker struct
method Enter (line 167) | func (c *aggregateChecker) Enter(n ast.Node) (ast.Node, bool) {
method Leave (line 189) | func (c *aggregateChecker) Leave(n ast.Node) (ast.Node, bool) {
function isDorisAggregateFunc (line 194) | func isDorisAggregateFunc(funcName string) bool {
function extractConstValue (line 264) | func extractConstValue(expr ast.ExprNode) (int64, bool) {
function preprocessDorisSQL (line 282) | func preprocessDorisSQL(sql string) string {
function NeedsRowCountCheck (line 300) | func NeedsRowCountCheck(sql string, maxQueryRows int) (bool, bool, strin...
FILE: dskit/doris/sql_analyzer_test.go
function TestAnalyzeSQL_AggregateQueries (line 7) | func TestAnalyzeSQL_AggregateQueries(t *testing.T) {
function TestAnalyzeSQL_SubqueryWithAggregate (line 173) | func TestAnalyzeSQL_SubqueryWithAggregate(t *testing.T) {
function TestAnalyzeSQL_LimitQueries (line 210) | func TestAnalyzeSQL_LimitQueries(t *testing.T) {
function TestAnalyzeSQL_UnionQueries (line 285) | func TestAnalyzeSQL_UnionQueries(t *testing.T) {
function TestAnalyzeSQL_NonSelectStatements (line 348) | func TestAnalyzeSQL_NonSelectStatements(t *testing.T) {
function TestNeedsRowCountCheck (line 385) | func TestNeedsRowCountCheck(t *testing.T) {
function TestNeedsRowCountCheck_DorisSpecificFunctions (line 486) | func TestNeedsRowCountCheck_DorisSpecificFunctions(t *testing.T) {
function TestNeedsRowCountCheck_ComplexQueries (line 544) | func TestNeedsRowCountCheck_ComplexQueries(t *testing.T) {
function TestNeedsRowCountCheck_EdgeCases (line 604) | func TestNeedsRowCountCheck_EdgeCases(t *testing.T) {
function TestNeedsRowCountCheck_DifferentMaxRows (line 664) | func TestNeedsRowCountCheck_DifferentMaxRows(t *testing.T) {
function TestSummary_SkipProbeCheck (line 716) | func TestSummary_SkipProbeCheck(t *testing.T) {
function ptr (line 782) | func ptr(v int64) *int64 {
FILE: dskit/doris/timeseries.go
constant TimeFieldFormatEpochMilli (line 13) | TimeFieldFormatEpochMilli = "epoch_millis"
constant TimeFieldFormatEpochSecond (line 14) | TimeFieldFormatEpochSecond = "epoch_second"
constant TimeFieldFormatDateTime (line 15) | TimeFieldFormatDateTime = "datetime"
type QueryParam (line 19) | type QueryParam struct
method Query (line 42) | func (d *Doris) Query(ctx context.Context, query *QueryParam) ([]map[str...
method QueryTimeseries (line 65) | func (d *Doris) QueryTimeseries(ctx context.Context, query *QueryParam) ...
method CheckMaxQueryRows (line 78) | func (d *Doris) CheckMaxQueryRows(ctx context.Context, database, sql str...
method probeRowCount (line 99) | func (d *Doris) probeRowCount(ctx context.Context, database, sql string,...
FILE: dskit/mysql/mysql.go
type MySQL (line 23) | type MySQL struct
method NewConn (line 62) | func (m *MySQL) NewConn(ctx context.Context, database string) (*gorm.D...
method ShowDatabases (line 128) | func (m *MySQL) ShowDatabases(ctx context.Context) ([]string, error) {
method ShowTables (line 137) | func (m *MySQL) ShowTables(ctx context.Context, database string) ([]st...
method DescTable (line 146) | func (m *MySQL) DescTable(ctx context.Context, database, table string)...
method SelectRows (line 156) | func (m *MySQL) SelectRows(ctx context.Context, database, table, query...
method ExecQuery (line 165) | func (m *MySQL) ExecQuery(ctx context.Context, database string, sql st...
type Shard (line 27) | type Shard struct
function NewMySQLWithSettings (line 39) | func NewMySQLWithSettings(ctx context.Context, settings interface{}) (*M...
FILE: dskit/mysql/mysql_test.go
function TestNewMySQLWithSettings (line 12) | func TestNewMySQLWithSettings(t *testing.T) {
function TestNewConn (line 41) | func TestNewConn(t *testing.T) {
function TestShowDatabases (line 70) | func TestShowDatabases(t *testing.T) {
function TestShowTables (line 81) | func TestShowTables(t *testing.T) {
function TestDescTable (line 92) | func TestDescTable(t *testing.T) {
function TestExecQuery (line 105) | func TestExecQuery(t *testing.T) {
function TestSelectRows (line 118) | func TestSelectRows(t *testing.T) {
FILE: dskit/mysql/timeseries.go
method Query (line 15) | func (m *MySQL) Query(ctx context.Context, query *sqlbase.QueryParam) ([...
method QueryTimeseries (line 30) | func (m *MySQL) QueryTimeseries(ctx context.Context, query *sqlbase.Quer...
method CheckMaxQueryRows (line 44) | func (m *MySQL) CheckMaxQueryRows(db *gorm.DB, ctx context.Context, quer...
FILE: dskit/mysql/timeseries_test.go
function TestQuery (line 15) | func TestQuery(t *testing.T) {
function TestQueryTimeseries (line 38) | func TestQueryTimeseries(t *testing.T) {
FILE: dskit/pool/pool.go
function PoolGetBytesBuffer (line 26) | func PoolGetBytesBuffer() *bytes.Buffer {
function PoolPutBytesBuffer (line 32) | func PoolPutBytesBuffer(buf *bytes.Buffer) {
FILE: dskit/postgres/postgres.go
type PostgreSQL (line 24) | type PostgreSQL struct
method NewConn (line 74) | func (p *PostgreSQL) NewConn(ctx context.Context, database string) (*g...
method ShowDatabases (line 145) | func (p *PostgreSQL) ShowDatabases(ctx context.Context, searchKeyword ...
method ShowTables (line 156) | func (p *PostgreSQL) ShowTables(ctx context.Context, searchKeyword str...
method DescTable (line 178) | func (p *PostgreSQL) DescTable(ctx context.Context, scheme, table stri...
method SelectRows (line 192) | func (p *PostgreSQL) SelectRows(ctx context.Context, table, where stri...
method ExecQuery (line 202) | func (p *PostgreSQL) ExecQuery(ctx context.Context, sql string) ([]map...
type Shard (line 28) | type Shard struct
function NewPostgreSQLWithSettings (line 41) | func NewPostgreSQLWithSettings(ctx context.Context, settings interface{}...
FILE: dskit/postgres/timeseries.go
method Query (line 14) | func (p *PostgreSQL) Query(ctx context.Context, query *sqlbase.QueryPara...
method QueryTimeseries (line 29) | func (p *PostgreSQL) QueryTimeseries(ctx context.Context, query *sqlbase...
method CheckMaxQueryRows (line 43) | func (p *PostgreSQL) CheckMaxQueryRows(db *gorm.DB, ctx context.Context,...
FILE: dskit/sqlbase/base.go
function NewDB (line 18) | func NewDB(ctx context.Context, dialector gorm.Dialector, maxIdleConns, ...
function CloseDB (line 38) | func CloseDB(db *gorm.DB) error {
function ShowTables (line 50) | func ShowTables(ctx context.Context, db *gorm.DB, query string) ([]strin...
function ShowDatabases (line 71) | func ShowDatabases(ctx context.Context, db *gorm.DB, query string) ([]st...
function DescTable (line 92) | func DescTable(ctx context.Context, db *gorm.DB, query string) ([]*types...
function ExecQuery (line 138) | func ExecQuery(ctx context.Context, db *gorm.DB, sql string) ([]map[stri...
function SelectRows (line 179) | func SelectRows(ctx context.Context, db *gorm.DB, table, query string) (...
function ConvertDBType (line 189) | func ConvertDBType(dialect, dbType string) (string, bool) {
FILE: dskit/sqlbase/timeseries.go
type QueryParam (line 23) | type QueryParam struct
function Query (line 44) | func Query(ctx context.Context, db *gorm.DB, query *QueryParam) ([]map[s...
function QueryTimeseries (line 57) | func QueryTimeseries(ctx context.Context, db *gorm.DB, query *QueryParam...
function FormatMetricValues (line 66) | func FormatMetricValues(keys types.Keys, rows []map[string]interface{}, ...
function ParseFloat64Value (line 190) | func ParseFloat64Value(val interface{}) (float64, error) {
function ParseTime (line 220) | func ParseTime(val interface{}, format string) (time.Time, error) {
function parseTimeFromString (line 249) | func parseTimeFromString(str, format string) (time.Time, error) {
FILE: dskit/sqlbase/timeseries_test.go
function TestFormatMetricValues (line 13) | func TestFormatMetricValues(t *testing.T) {
function TestParseFloat64Value (line 114) | func TestParseFloat64Value(t *testing.T) {
function TestParseTime (line 155) | func TestParseTime(t *testing.T) {
FILE: dskit/tdengine/tdengine.go
type Tdengine (line 19) | type Tdengine struct
method InitCli (line 53) | func (tc *Tdengine) InitCli() {
method QueryTable (line 87) | func (tc *Tdengine) QueryTable(query string) (APIResponse, error) {
method ShowDatabases (line 124) | func (tc *Tdengine) ShowDatabases(context.Context) ([]string, error) {
method ShowTables (line 137) | func (tc *Tdengine) ShowTables(ctx context.Context, database string) (...
method DescribeTable (line 151) | func (tc *Tdengine) DescribeTable(ctx context.Context, query interface...
type TDengineBasicAuth (line 35) | type TDengineBasicAuth struct
type APIResponse (line 41) | type APIResponse struct
type QueryParam (line 48) | type QueryParam struct
FILE: dskit/types/timeseries.go
type MetricValues (line 12) | type MetricValues struct
method String (line 29) | func (m *MetricValues) String() string {
type HistogramValues (line 17) | type HistogramValues struct
type AggregateValues (line 23) | type AggregateValues struct
type Keys (line 46) | type Keys struct
FILE: dskit/types/types.go
constant LogExtractValueTypeLong (line 4) | LogExtractValueTypeLong = "long"
constant LogExtractValueTypeFloat (line 5) | LogExtractValueTypeFloat = "float"
constant LogExtractValueTypeText (line 6) | LogExtractValueTypeText = "text"
constant LogExtractValueTypeDate (line 7) | LogExtractValueTypeDate = "date"
constant LogExtractValueTypeBool (line 8) | LogExtractValueTypeBool = "bool"
constant LogExtractValueTypeObject (line 9) | LogExtractValueTypeObject = "object"
constant LogExtractValueTypeArray (line 10) | LogExtractValueTypeArray = "array"
constant LogExtractValueTypeJSON (line 11) | LogExtractValueTypeJSON = "json"
type ColumnProperty (line 14) | type ColumnProperty struct
FILE: dskit/victorialogs/victorialogs.go
type VictoriaLogs (line 17) | type VictoriaLogs struct
method InitHTTPClient (line 66) | func (vl *VictoriaLogs) InitHTTPClient() error {
method Query (line 91) | func (vl *VictoriaLogs) Query(ctx context.Context, query string, start...
method StatsQuery (line 147) | func (vl *VictoriaLogs) StatsQuery(ctx context.Context, query string, ...
method StatsQueryRange (line 186) | func (vl *VictoriaLogs) StatsQueryRange(ctx context.Context, query str...
method HitsLogs (line 231) | func (vl *VictoriaLogs) HitsLogs(ctx context.Context, query string, st...
method doRequest (line 272) | func (vl *VictoriaLogs) doRequest(ctx context.Context, method, endpoin...
type LogEntry (line 36) | type LogEntry
type PrometheusResponse (line 39) | type PrometheusResponse struct
type PrometheusData (line 46) | type PrometheusData struct
type PrometheusItem (line 52) | type PrometheusItem struct
type HitsResult (line 59) | type HitsResult struct
FILE: dskit/victorialogs/victorialogs_test.go
function TestVictoriaLogs_InitHTTPClient (line 15) | func TestVictoriaLogs_InitHTTPClient(t *testing.T) {
function TestVictoriaLogs_Query (line 24) | func TestVictoriaLogs_Query(t *testing.T) {
function TestVictoriaLogs_StatsQuery (line 45) | func TestVictoriaLogs_StatsQuery(t *testing.T) {
function TestVictoriaLogs_StatsQueryRange (line 63) | func TestVictoriaLogs_StatsQueryRange(t *testing.T) {
function TestVictoriaLogs_HitsLogs (line 84) | func TestVictoriaLogs_HitsLogs(t *testing.T) {
function TestVictoriaLogs_QueryWithFilter (line 102) | func TestVictoriaLogs_QueryWithFilter(t *testing.T) {
function TestVictoriaLogs_StatsQueryByField (line 120) | func TestVictoriaLogs_StatsQueryByField(t *testing.T) {
FILE: dumper/dumper.go
function ConfigRouter (line 6) | func ConfigRouter(r *gin.Engine) {
FILE: dumper/sync.go
type SyncRecord (line 12) | type SyncRecord struct
method String (line 19) | func (sr *SyncRecord) String() string {
type SyncRecords (line 33) | type SyncRecords struct
type SyncDumper (line 38) | type SyncDumper struct
method Put (line 51) | func (sd *SyncDumper) Put(key string, timestamp, mills int64, count in...
method Sprint (line 74) | func (sd *SyncDumper) Sprint() string {
method ConfigRouter (line 97) | func (sd *SyncDumper) ConfigRouter(r *gin.Engine) {
function NewSyncDumper (line 43) | func NewSyncDumper() *SyncDumper {
function PutSyncRecord (line 108) | func PutSyncRecord(key string, timestamp, mills int64, count int, messag...
FILE: etc/script/notify.bak.py
class Sender (line 27) | class Sender(object):
method send_email (line 29) | def send_email(cls, payload):
method send_wecom (line 60) | def send_wecom(cls, payload):
method send_dingtalk (line 91) | def send_dingtalk(cls, payload):
method send_feishu (line 137) | def send_feishu(cls, payload):
method send_sms (line 176) | def send_sms(cls, payload):
method send_voice (line 186) | def send_voice(cls, payload):
function main (line 195) | def main():
function hello (line 207) | def hello():
FILE: etc/script/notify.py
class Sender (line 6) | class Sender(object):
method send_email (line 8) | def send_email(cls, payload):
method send_wecom (line 13) | def send_wecom(cls, payload):
method send_dingtalk (line 18) | def send_dingtalk(cls, payload):
method send_feishu (line 23) | def send_feishu(cls, payload):
method send_mm (line 28) | def send_mm(cls, payload):
method send_sms (line 33) | def send_sms(cls, payload):
method send_voice (line 43) | def send_voice(cls, payload):
function main (line 52) | def main():
function hello (line 64) | def hello():
FILE: etc/script/notify_feishu.py
class Sender (line 7) | class Sender(object):
method send_email (line 9) | def send_email(cls, payload):
method send_wecom (line 14) | def send_wecom(cls, payload):
method send_dingtalk (line 19) | def send_dingtalk(cls, payload):
method send_ifeishu (line 24) | def send_ifeishu(cls, payload):
method send_mm (line 59) | def send_mm(cls, payload):
method send_sms (line 64) | def send_sms(cls, payload):
method send_voice (line 68) | def send_voice(cls, payload):
function main (line 71) | def main():
function hello (line 83) | def hello():
FILE: etc/script/rule_converter.py
function convert_interval (line 12) | def convert_interval(interval):
function convert_alert (line 24) | def convert_alert(rule, interval):
function convert_record (line 92) | def convert_record(rule, interval):
function deal_group (line 127) | def deal_group(group):
function deal_configmap (line 169) | def deal_configmap(rule_configmap):
function main (line 184) | def main():
FILE: memsto/alert_mute_cache.go
type AlertMuteCacheType (line 16) | type AlertMuteCacheType struct
method Reset (line 38) | func (amc *AlertMuteCacheType) Reset() {
method StatChanged (line 47) | func (amc *AlertMuteCacheType) StatChanged(total, lastUpdated int64) b...
method Set (line 55) | func (amc *AlertMuteCacheType) Set(ms map[int64][]*models.AlertMute, t...
method Gets (line 65) | func (amc *AlertMuteCacheType) Gets(bgid int64) ([]*models.AlertMute, ...
method GetAllStructs (line 72) | func (amc *AlertMuteCacheType) GetAllStructs() map[int64][]models.Aler...
method SyncAlertMutes (line 87) | func (amc *AlertMuteCacheType) SyncAlertMutes() {
method loopSyncAlertMutes (line 97) | func (amc *AlertMuteCacheType) loopSyncAlertMutes() {
method syncAlertMutes (line 107) | func (amc *AlertMuteCacheType) syncAlertMutes() error {
function NewAlertMuteCache (line 26) | func NewAlertMuteCache(ctx *ctx.Context, stats *Stats) *AlertMuteCacheTy...
FILE: memsto/alert_rule_cache.go
type AlertRuleCacheType (line 16) | type AlertRuleCacheType struct
method Reset (line 38) | func (arc *AlertRuleCacheType) Reset() {
method StatChanged (line 47) | func (arc *AlertRuleCacheType) StatChanged(total, lastUpdated int64) b...
method Set (line 55) | func (arc *AlertRuleCacheType) Set(m map[int64]*models.AlertRule, tota...
method Get (line 65) | func (arc *AlertRuleCacheType) Get(ruleId int64) *models.AlertRule {
method GetRuleIds (line 71) | func (arc *AlertRuleCacheType) GetRuleIds() []int64 {
method SyncAlertRules (line 84) | func (arc *AlertRuleCacheType) SyncAlertRules() {
method loopSyncAlertRules (line 94) | func (arc *AlertRuleCacheType) loopSyncAlertRules() {
method syncAlertRules (line 104) | func (arc *AlertRuleCacheType) syncAlertRules() error {
function NewAlertRuleCache (line 26) | func NewAlertRuleCache(ctx *ctx.Context, stats *Stats) *AlertRuleCacheTy...
FILE: memsto/alert_subscribe_cache.go
type AlertSubscribeCacheType (line 16) | type AlertSubscribeCacheType struct
method Reset (line 38) | func (c *AlertSubscribeCacheType) Reset() {
method StatChanged (line 47) | func (c *AlertSubscribeCacheType) StatChanged(total, lastUpdated int64...
method Set (line 55) | func (c *AlertSubscribeCacheType) Set(m map[int64][]*models.AlertSubsc...
method Get (line 65) | func (c *AlertSubscribeCacheType) Get(ruleId int64) ([]*models.AlertSu...
method GetAll (line 73) | func (c *AlertSubscribeCacheType) GetAll() []*models.AlertSubscribe {
method GetStructs (line 83) | func (c *AlertSubscribeCacheType) GetStructs(ruleId int64) []models.Al...
method SyncAlertSubscribes (line 100) | func (c *AlertSubscribeCacheType) SyncAlertSubscribes() {
method loopSyncAlertSubscribes (line 110) | func (c *AlertSubscribeCacheType) loopSyncAlertSubscribes() {
method syncAlertSubscribes (line 120) | func (c *AlertSubscribeCacheType) syncAlertSubscribes() error {
function NewAlertSubscribeCache (line 26) | func NewAlertSubscribeCache(ctx *ctx.Context, stats *Stats) *AlertSubscr...
FILE: memsto/busi_group_cache.go
type BusiGroupCacheType (line 16) | type BusiGroupCacheType struct
method StatChanged (line 39) | func (c *BusiGroupCacheType) StatChanged(total, lastUpdated int64) bool {
method Set (line 47) | func (c *BusiGroupCacheType) Set(ugs map[int64]*models.BusiGroup, tota...
method GetByBusiGroupId (line 57) | func (c *BusiGroupCacheType) GetByBusiGroupId(id int64) *models.BusiGr...
method GetNamesByBusiGroupIds (line 63) | func (c *BusiGroupCacheType) GetNamesByBusiGroupIds(ids []int64) []str...
method SyncBusiGroups (line 75) | func (c *BusiGroupCacheType) SyncBusiGroups() {
method loopSyncBusiGroups (line 84) | func (c *BusiGroupCacheType) loopSyncBusiGroups() {
method syncBusiGroups (line 94) | func (c *BusiGroupCacheType) syncBusiGroups() error {
method GetNameByBusiGroupId (line 126) | func (c *BusiGroupCacheType) GetNameByBusiGroupId(id int64) string {
function NewBusiGroupCache (line 26) | func NewBusiGroupCache(ctx *ctx.Context, stats *Stats) *BusiGroupCacheTy...
FILE: memsto/config_cache.go
type ConfigCache (line 16) | type ConfigCache struct
method initSyncConfigs (line 42) | func (c *ConfigCache) initSyncConfigs() {
method loopSyncConfigs (line 52) | func (c *ConfigCache) loopSyncConfigs() {
method syncConfigs (line 62) | func (c *ConfigCache) syncConfigs() error {
method statChanged (line 94) | func (c *ConfigCache) statChanged(total int64, updated int64) bool {
method Set (line 101) | func (c *ConfigCache) Set(decryptMap map[string]string, total int64, u...
method Get (line 109) | func (c *ConfigCache) Get() map[string]string {
method GetLastUpdateTime (line 119) | func (c *ConfigCache) GetLastUpdateTime() int64 {
function NewConfigCache (line 28) | func NewConfigCache(ctx *ctx.Context, status *Stats, privateKey []byte, ...
FILE: memsto/config_cval_cache.go
type CvalCache (line 17) | type CvalCache struct
method initSyncConfigs (line 39) | func (c *CvalCache) initSyncConfigs() {
method loopSyncConfigs (line 53) | func (c *CvalCache) loopSyncConfigs() {
method syncConfigs (line 63) | func (c *CvalCache) syncConfigs() error {
method statChanged (line 95) | func (c *CvalCache) statChanged(total int64, updated int64) bool {
method Set (line 102) | func (c *CvalCache) Set(cvals []*models.Configs, total int64, updated ...
method Get (line 112) | func (c *CvalCache) Get(ckey string) string {
method GetLastUpdateTime (line 118) | func (c *CvalCache) GetLastUpdateTime() int64 {
method GetSiteInfo (line 131) | func (c *CvalCache) GetSiteInfo() *SiteInfo {
method PrintBodyPaths (line 144) | func (c *CvalCache) PrintBodyPaths() map[string]struct{} {
method PrintAccessLog (line 153) | func (c *CvalCache) PrintAccessLog() bool {
function NewCvalCache (line 27) | func NewCvalCache(ctx *ctx.Context, stats *Stats) *CvalCache {
type SiteInfo (line 124) | type SiteInfo struct
FILE: memsto/datasource_cache.go
type DatasourceCacheType (line 17) | type DatasourceCacheType struct
method GetIDsByDsCateAndQueries (line 47) | func (d *DatasourceCacheType) GetIDsByDsCateAndQueries(cate string, da...
method StatChanged (line 53) | func (d *DatasourceCacheType) StatChanged(total, lastUpdated int64) bo...
method Set (line 61) | func (d *DatasourceCacheType) Set(ds map[int64]*models.Datasource, tot...
method GetById (line 85) | func (d *DatasourceCacheType) GetById(id int64) *models.Datasource {
method SyncDatasources (line 91) | func (d *DatasourceCacheType) SyncDatasources() {
method loopSyncDatasources (line 100) | func (d *DatasourceCacheType) loopSyncDatasources() {
method syncDatasources (line 110) | func (d *DatasourceCacheType) syncDatasources() error {
function NewDatasourceCache (line 31) | func NewDatasourceCache(ctx *ctx.Context, stats *Stats) *DatasourceCache...
FILE: memsto/drop_ident.go
type Item (line 8) | type Item struct
type IdentCountCacheType (line 13) | type IdentCountCacheType struct
method Set (line 27) | func (c *IdentCountCacheType) Set(ident string, count int, ts int64) {
method Increment (line 37) | func (c *IdentCountCacheType) Increment(ident string, num int) {
method Exists (line 55) | func (c *IdentCountCacheType) Exists(ident string) bool {
method Get (line 62) | func (c *IdentCountCacheType) Get(ident string) int {
method GetsAndFlush (line 72) | func (c *IdentCountCacheType) GetsAndFlush() map[string]Item {
method CronDeleteExpired (line 83) | func (c *IdentCountCacheType) CronDeleteExpired() {
method deleteExpired (line 91) | func (c *IdentCountCacheType) deleteExpired() {
function NewIdentCountCache (line 18) | func NewIdentCountCache() *IdentCountCacheType {
FILE: memsto/es_index_pattern.go
type EsIndexPatternCacheType (line 13) | type EsIndexPatternCacheType struct
method Reset (line 30) | func (p *EsIndexPatternCacheType) Reset() {
method Set (line 37) | func (p *EsIndexPatternCacheType) Set(m map[int64]*models.EsIndexPatte...
method Get (line 43) | func (p *EsIndexPatternCacheType) Get(id int64) (*models.EsIndexPatter...
method SyncEsIndexPattern (line 51) | func (p *EsIndexPatternCacheType) SyncEsIndexPattern() {
method loopSyncEsIndexPattern (line 60) | func (p *EsIndexPatternCacheType) loopSyncEsIndexPattern() {
method syncEsIndexPattern (line 70) | func (p *EsIndexPatternCacheType) syncEsIndexPattern() error {
function NewEsIndexPatternCacheType (line 20) | func NewEsIndexPatternCacheType(ctx *ctx.Context) *EsIndexPatternCacheTy...
FILE: memsto/event_processor_cache.go
type EventProcessorCacheType (line 16) | type EventProcessorCacheType struct
method Reset (line 38) | func (epc *EventProcessorCacheType) Reset() {
method StatChanged (line 47) | func (epc *EventProcessorCacheType) StatChanged(total, lastUpdated int...
method Set (line 55) | func (epc *EventProcessorCacheType) Set(m map[int64]*models.EventPipel...
method Get (line 65) | func (epc *EventProcessorCacheType) Get(processorId int64) *models.Eve...
method GetProcessorIds (line 71) | func (epc *EventProcessorCacheType) GetProcessorIds() []int64 {
method SyncEventProcessors (line 84) | func (epc *EventProcessorCacheType) SyncEventProcessors() {
method loopSyncEventProcessors (line 94) | func (epc *EventProcessorCacheType) loopSyncEventProcessors() {
method syncEventProcessors (line 104) | func (epc *EventProcessorCacheType) syncEventProcessors() error {
function NewEventProcessorCache (line 26) | func NewEventProcessorCache(ctx *ctx.Context, stats *Stats) *EventProces...
FILE: memsto/host_alert_rule_targets.go
type TargetsOfAlertRuleCacheType (line 13) | type TargetsOfAlertRuleCacheType struct
method Reset (line 38) | func (tc *TargetsOfAlertRuleCacheType) Reset() {
method Set (line 47) | func (tc *TargetsOfAlertRuleCacheType) Set(m map[string]map[int64][]st...
method Get (line 57) | func (tc *TargetsOfAlertRuleCacheType) Get(engineName string, rid int6...
method SyncTargets (line 69) | func (tc *TargetsOfAlertRuleCacheType) SyncTargets() {
method loopSyncTargets (line 78) | func (tc *TargetsOfAlertRuleCacheType) loopSyncTargets() {
method syncTargets (line 88) | func (tc *TargetsOfAlertRuleCacheType) syncTargets() error {
function NewTargetOfAlertRuleCache (line 24) | func NewTargetOfAlertRuleCache(ctx *ctx.Context, engineName string, stat...
FILE: memsto/memsto.go
function exit (line 10) | func exit(code int) {
FILE: memsto/message_template_cache.go
type Messa
Copy disabled (too large)
Download .json
Condensed preview — 802 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (11,056K chars).
[
{
"path": ".gitattributes",
"chars": 140,
"preview": "*.css linguist-language=go\n*.less linguist-language=go\n*.js linguist-language=go\n*.tsx linguist-language=go\n*.html l"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 179,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Nightingale docs\n url: https://n9e.github.io/\n about: You may"
},
{
"path": ".github/ISSUE_TEMPLATE/enhancement.md",
"chars": 250,
"preview": "---\nname: Enhancement Request\nabout: Suggest an enhancement to the nightingale project\nlabels: kind/feature\n\n---\n<!-- Pl"
},
{
"path": ".github/ISSUE_TEMPLATE/question.yml",
"chars": 1120,
"preview": "name: Bug Report & Usage Question\ndescription: Reporting a bug or asking a question about how to use Nightingale \nlabels"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 288,
"preview": "**What type of PR is this?**\n\n**What this PR does / why we need it**:\n<!--\n\"Nice to have\" \"You need it\" is not a good re"
},
{
"path": ".github/workflows/issue-translator.yml",
"chars": 483,
"preview": "name: 'Issue Translator'\n\non:\n issues:\n types: [opened]\n\njobs:\n translate:\n runs-on: ubuntu-latest\n permissio"
},
{
"path": ".github/workflows/n9e.yml",
"chars": 774,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\nenv:\n GO_VERSION: 1.23\n\njobs:\n goreleaser:\n runs-on: ubuntu-lates"
},
{
"path": ".gitignore",
"chars": 882,
"preview": "*.exe\n*.exe~\n*.dll\n*.dylib\n*.test\n*.out\n*.prof\n*.log\n*.o\n*.a\n*.so\n*.db\n*.sw[po]\n*.tar.gz\n*.[568vq]\n[568vq].out\n\n*.cgo1.g"
},
{
"path": ".goreleaser.yaml",
"chars": 2594,
"preview": "before:\n hooks:\n # You may remove this if you don't use go modules.\n - go mod tidy\n - go install github.com/ra"
},
{
"path": ".typos.toml",
"chars": 982,
"preview": "# Configuration for typos tool\n[files]\nextend-exclude = [\n # Ignore auto-generated easyjson files\n \"*_easyjson.go\""
},
{
"path": "LICENSE",
"chars": 11333,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 1414,
"preview": ".PHONY: prebuild build\n\nROOT:=$(shell pwd -P)\nGIT_COMMIT:=$(shell git --work-tree ${ROOT} rev-parse 'HEAD^{commit}')\n_G"
},
{
"path": "README.md",
"chars": 9036,
"preview": "<p align=\"center\">\n <a href=\"https://github.com/ccfos/nightingale\">\n <img src=\"doc/img/Nightingale_L_V.png\" alt=\"nig"
},
{
"path": "README_zh.md",
"chars": 5168,
"preview": "<p align=\"center\">\n <a href=\"https://github.com/ccfos/nightingale\">\n <img src=\"doc/img/Nightingale_L_V.png\" alt=\"nig"
},
{
"path": "alert/aconf/conf.go",
"chars": 1316,
"preview": "package aconf\n\nimport (\n\t\"path\"\n)\n\ntype Alert struct {\n\tDisable bool\n\tEngineDelay int64\n\tHeartbeat HeartbeatConfig"
},
{
"path": "alert/alert.go",
"chars": 5960,
"preview": "package alert\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/dscache\"\n\n\t\"github.com/ccfos/nightingale/v6"
},
{
"path": "alert/astats/stats.go",
"chars": 6497,
"preview": "package astats\n\nimport (\n\t\"github.com/prometheus/client_golang/prometheus\"\n)\n\nconst (\n\tnamespace = \"n9e\"\n\tsubsystem = \"a"
},
{
"path": "alert/common/key.go",
"chars": 2861,
"preview": "package common\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\nfunc RuleKey(d"
},
{
"path": "alert/dispatch/consume.go",
"chars": 6282,
"preview": "package dispatch\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/aler"
},
{
"path": "alert/dispatch/dispatch.go",
"chars": 30853,
"preview": "package dispatch\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n"
},
{
"path": "alert/dispatch/log.go",
"chars": 689,
"preview": "package dispatch\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/models\"\n\n\t\"github.com/toolkits/pkg/logger\"\n)\n\nfunc LogEvent"
},
{
"path": "alert/dispatch/notify_channel.go",
"chars": 702,
"preview": "package dispatch\n\n// NotifyChannels channelKey -> bool\ntype NotifyChannels map[string]bool\n\nfunc NewNotifyChannels(chann"
},
{
"path": "alert/dispatch/notify_target.go",
"chars": 3635,
"preview": "package dispatch\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\n// NotifyTarget 维护所有需要发送的目标 用户-通道/回调"
},
{
"path": "alert/eval/alert_rule.go",
"chars": 6250,
"preview": "package eval\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.com"
},
{
"path": "alert/eval/eval.go",
"chars": 58778,
"preview": "package eval\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"s"
},
{
"path": "alert/eval/eval_test.go",
"chars": 10547,
"preview": "package eval\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"golang.org/x/exp/slices\"\n)\n\nvar (\n\treHashTagIndex1 = map[uint64][][]uint"
},
{
"path": "alert/mute/mute.go",
"chars": 6015,
"preview": "package mute\n\nimport (\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/common\"\n\t\"github"
},
{
"path": "alert/naming/hashring.go",
"chars": 2083,
"preview": "package naming\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\n\t\"github.com/toolkits/pkg/consistent\"\n\t\"github.com/toolkits/pkg/logger\"\n)\n\nc"
},
{
"path": "alert/naming/heartbeat.go",
"chars": 5218,
"preview": "package naming\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.com/"
},
{
"path": "alert/naming/leader.go",
"chars": 446,
"preview": "package naming\n\nimport (\n\t\"sort\"\n\n\t\"github.com/toolkits/pkg/logger\"\n)\n\nfunc (n *Naming) IamLeader() bool {\n\tif !n.ctx.Is"
},
{
"path": "alert/pipeline/engine/engine.go",
"chars": 9904,
"preview": "package engine\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg"
},
{
"path": "alert/pipeline/pipeline.go",
"chars": 473,
"preview": "package pipeline\n\nimport (\n\t_ \"github.com/ccfos/nightingale/v6/alert/pipeline/processor/aisummary\"\n\t_ \"github.com/ccfos/"
},
{
"path": "alert/pipeline/processor/aisummary/ai_summary.go",
"chars": 5969,
"preview": "package aisummary\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"str"
},
{
"path": "alert/pipeline/processor/aisummary/ai_summary_test.go",
"chars": 3337,
"preview": "package aisummary\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/pipeline/processor/callback\"\n\t\"github.co"
},
{
"path": "alert/pipeline/processor/callback/callback.go",
"chars": 3016,
"preview": "package callback\n\nimport (\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gith"
},
{
"path": "alert/pipeline/processor/common/common.go",
"chars": 385,
"preview": "package common\n\nimport (\n\t\"encoding/json\"\n)\n\n// InitProcessor 是一个通用的初始化处理器的方法\n// 使用泛型简化处理器初始化逻辑\n// T 必须是 models.Processo"
},
{
"path": "alert/pipeline/processor/eventdrop/event_drop.go",
"chars": 1751,
"preview": "package eventdrop\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\ttexttemplate \"text/template\"\n\n\t\"github.com/ccfos/nightingale/v6/"
},
{
"path": "alert/pipeline/processor/eventupdate/event_update.go",
"chars": 2472,
"preview": "package eventupdate\n\nimport (\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"g"
},
{
"path": "alert/pipeline/processor/logic/if.go",
"chars": 5139,
"preview": "package logic\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"text/template\"\n\n\talertCommon \"github.com/ccfos/nightingale/v6/alert"
},
{
"path": "alert/pipeline/processor/logic/switch.go",
"chars": 6238,
"preview": "package logic\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"text/template\"\n\n\talertCommon \"github.com/ccfos/nightingale/v6/alert"
},
{
"path": "alert/pipeline/processor/relabel/relabel.go",
"chars": 3050,
"preview": "package relabel\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/pipeline/processor/common"
},
{
"path": "alert/pipeline/processor/utils/utils.go",
"chars": 791,
"preview": "package utils\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"text/template\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github."
},
{
"path": "alert/process/alert_cur_event.go",
"chars": 1411,
"preview": "package process\n\nimport (\n\t\"sync\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype AlertCurEventMap struct {\n\tsync.RWM"
},
{
"path": "alert/process/process.go",
"chars": 20350,
"preview": "package process\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github"
},
{
"path": "alert/queue/queue.go",
"chars": 320,
"preview": "package queue\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/astats\"\n\t\"github.com/toolkits/pkg/container/lis"
},
{
"path": "alert/record/prom_rule.go",
"chars": 3259,
"preview": "package record\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/astats\"\n\t\"github."
},
{
"path": "alert/record/sample.go",
"chars": 2521,
"preview": "package record\n\nimport (\n\t\"math\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\n\t\"github.com/prometheus/"
},
{
"path": "alert/record/scheduler.go",
"chars": 2374,
"preview": "package record\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.c"
},
{
"path": "alert/router/router.go",
"chars": 2357,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.com/ccfos/nightingale/v6/a"
},
{
"path": "alert/router/router_alert_eval_detail.go",
"chars": 613,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/loggrep\"\n\t\"github.com/ccfos/nightingale/v6/pkg/gi"
},
{
"path": "alert/router/router_event.go",
"chars": 4631,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/dispatch\"\n\t\"githu"
},
{
"path": "alert/router/router_event_detail.go",
"chars": 562,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/loggrep\"\n\t\"github.com/ccfos/nightingale/v6/pkg/gi"
},
{
"path": "alert/router/router_trace_logs.go",
"chars": 621,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/ginx\"\n\t\"github.com/ccfos/nightingale/v6/pkg/loggr"
},
{
"path": "alert/sender/callback.go",
"chars": 5833,
"preview": "package sender\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/"
},
{
"path": "alert/sender/dingtalk.go",
"chars": 2762,
"preview": "package sender\n\nimport (\n\t\"html/template\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype dingtalkMarkdown"
},
{
"path": "alert/sender/email.go",
"chars": 5681,
"preview": "package sender\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"html/template\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf"
},
{
"path": "alert/sender/feishu.go",
"chars": 2261,
"preview": "package sender\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype feishuCon"
},
{
"path": "alert/sender/feishucard.go",
"chars": 4283,
"preview": "package sender\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntyp"
},
{
"path": "alert/sender/global_webhook.go",
"chars": 4187,
"preview": "package sender\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/ni"
},
{
"path": "alert/sender/global_webhook_test.go",
"chars": 3228,
"preview": "package sender\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/ccfos/nightingale/v6/alert"
},
{
"path": "alert/sender/ibex.go",
"chars": 7239,
"preview": "// @Author: Ciusyan 6/5/24\n\npackage sender\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com"
},
{
"path": "alert/sender/lark.go",
"chars": 1488,
"preview": "package sender\n\nimport (\n\t\"html/template\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\nvar (\n\t_ CallBacker ="
},
{
"path": "alert/sender/larkcard.go",
"chars": 2931,
"preview": "package sender\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntyp"
},
{
"path": "alert/sender/mm.go",
"chars": 2714,
"preview": "package sender\n\nimport (\n\t\"html/template\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/astats\"\n\t\"githu"
},
{
"path": "alert/sender/notify_record_queue.go",
"chars": 1735,
"preview": "package sender\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/astats\"\n\t\"github.com/ccfos/nightinga"
},
{
"path": "alert/sender/plugin.go",
"chars": 3488,
"preview": "package sender\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"github.com/ccfos/nightingale/v6/ale"
},
{
"path": "alert/sender/plugin_cmd_unix.go",
"chars": 198,
"preview": "//go:build !windows\n// +build !windows\n\npackage sender\n\nimport (\n\t\"os/exec\"\n\t\"syscall\"\n)\n\nfunc startCmd(c *exec.Cmd) err"
},
{
"path": "alert/sender/plugin_cmd_windows.go",
"chars": 89,
"preview": "package sender\n\nimport \"os/exec\"\n\nfunc startCmd(c *exec.Cmd) error {\n\treturn c.Start()\n}\n"
},
{
"path": "alert/sender/sender.go",
"chars": 2295,
"preview": "package sender\n\nimport (\n\t\"bytes\"\n\t\"html/template\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.com/ccfos/ni"
},
{
"path": "alert/sender/telegram.go",
"chars": 2564,
"preview": "package sender\n\nimport (\n\t\"errors\"\n\t\"html/template\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/astats\"\n\t\"github"
},
{
"path": "alert/sender/webhook.go",
"chars": 6289,
"preview": "package sender\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/"
},
{
"path": "alert/sender/webhook_event_queue.go",
"chars": 2076,
"preview": "package sender\n\nimport (\n\t\"container/list\"\n\t\"sync\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype SafeEventQueue str"
},
{
"path": "alert/sender/webhook_event_queue_test.go",
"chars": 3299,
"preview": "package sender\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/stretchr/tes"
},
{
"path": "alert/sender/webhook_queue.go",
"chars": 1846,
"preview": "package sender\n\nimport (\n\t\"container/list\"\n\t\"sync\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype SafeList struct {\n"
},
{
"path": "alert/sender/wecom.go",
"chars": 1704,
"preview": "package sender\n\nimport (\n\t\"html/template\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype wecomMarkdown st"
},
{
"path": "center/cconf/conf.go",
"chars": 1014,
"preview": "package cconf\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/httpx\"\n)\n\ntype Center struct {\n\tPlugins "
},
{
"path": "center/cconf/event_example.go",
"chars": 1437,
"preview": "package cconf\n\nconst EVENT_EXAMPLE = `\n{\n \"id\": 1000000,\n \"cate\": \"prometheus\",\n \"datasource_id\": 1,\n \"group"
},
{
"path": "center/cconf/metric.go",
"chars": 1074,
"preview": "package cconf\n\nimport (\n\t\"path\"\n\n\t\"github.com/toolkits/pkg/file\"\n)\n\n// metricDesc , As load map happens before read map,"
},
{
"path": "center/cconf/ops.go",
"chars": 8305,
"preview": "package cconf\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\n\t\"github.com/toolkits/pkg/file\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nvar Operations = Operatio"
},
{
"path": "center/cconf/plugin.go",
"chars": 987,
"preview": "package cconf\n\nvar Plugins = []Plugin{\n\t{\n\t\tId: 1,\n\t\tCategory: \"timeseries\",\n\t\tType: \"prometheus\",\n\t\tTypeName:"
},
{
"path": "center/cconf/rsa/rsa_conf.go",
"chars": 3426,
"preview": "package rsa\n\nimport (\n\t\"os\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ctx\"\n\t\"gith"
},
{
"path": "center/cconf/sql_tpl.go",
"chars": 2485,
"preview": "package cconf\n\nvar TDengineSQLTpl = map[string]string{\n\t\"load5\": \"SELECT _wstart as ts, last(load5) FROM "
},
{
"path": "center/center.go",
"chars": 7719,
"preview": "package center\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/dscache\"\n\n\t\"github.com/to"
},
{
"path": "center/cstats/stats.go",
"chars": 1319,
"preview": "package cstats\n\nimport (\n\t\"time\"\n\n\t\"github.com/prometheus/client_golang/prometheus\"\n)\n\nconst (\n\tnamespace = \"n9e\"\n\tsubsy"
},
{
"path": "center/integration/init.go",
"chars": 17918,
"preview": "package integration\n\nimport (\n\t\"encoding/json\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/mod"
},
{
"path": "center/metas/metas.go",
"chars": 2949,
"preview": "package metas\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/center/cstats\"\n\t\""
},
{
"path": "center/router/router.go",
"chars": 41032,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"path\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/ale"
},
{
"path": "center/router/router_alert_aggr_view.go",
"chars": 1660,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/gi"
},
{
"path": "center/router/router_alert_cur_event.go",
"chars": 10773,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"gith"
},
{
"path": "center/router/router_alert_eval_detail.go",
"chars": 4312,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/c"
},
{
"path": "center/router/router_alert_his_event.go",
"chars": 5122,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/c"
},
{
"path": "center/router/router_alert_rule.go",
"chars": 24405,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gopkg.in/yaml.v2"
},
{
"path": "center/router/router_alert_subscribe.go",
"chars": 8285,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/common\"\n\t\"gi"
},
{
"path": "center/router/router_board.go",
"chars": 8613,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nighti"
},
{
"path": "center/router/router_builtin.go",
"chars": 8719,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/model"
},
{
"path": "center/router/router_builtin_component.go",
"chars": 1927,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ct"
},
{
"path": "center/router/router_builtin_metric_filter.go",
"chars": 2627,
"preview": "package router\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/prom\"\n\t\"github."
},
{
"path": "center/router/router_builtin_metrics.go",
"chars": 3999,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/center/integration\"\n\t\"github.com"
},
{
"path": "center/router/router_builtin_payload.go",
"chars": 8078,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/BurntSushi/toml\"\n\t\"github.com/ccf"
},
{
"path": "center/router/router_busi_group.go",
"chars": 4306,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/st"
},
{
"path": "center/router/router_captcha.go",
"chars": 2370,
"preview": "package router\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/storage\"\n\t\"github.com/ccfos/nightingale/v"
},
{
"path": "center/router/router_chart_share.go",
"chars": 985,
"preview": "package router\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/strx\"\n"
},
{
"path": "center/router/router_config.go",
"chars": 1559,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/p"
},
{
"path": "center/router/router_configs.go",
"chars": 2594,
"preview": "package router\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ginx\"\n"
},
{
"path": "center/router/router_crypto.go",
"chars": 1093,
"preview": "package router\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/pkg/secu\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ginx\"\n\n\t\"gith"
},
{
"path": "center/router/router_dash_annotation.go",
"chars": 2455,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nighti"
},
{
"path": "center/router/router_dashboard.go",
"chars": 463,
"preview": "package router\n\ntype ChartPure struct {\n\tConfigs string `json:\"configs\"`\n\tWeight int `json:\"weight\"`\n}\n\ntype ChartGr"
},
{
"path": "center/router/router_datasource.go",
"chars": 13753,
"preview": "package router\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url"
},
{
"path": "center/router/router_datasource_db.go",
"chars": 2521,
"preview": "package router\n\nimport (\n\t\"context\"\n\n\t\"github.com/ccfos/nightingale/v6/dscache\"\n\t\"github.com/ccfos/nightingale/v6/dskit/"
},
{
"path": "center/router/router_embedded.go",
"chars": 3117,
"preview": "package router\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ctx\"\n\t"
},
{
"path": "center/router/router_es.go",
"chars": 1960,
"preview": "package router\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/datasource/es\"\n\t\"github.com/ccfos/nightingale/v6/dscache\"\n\t\"g"
},
{
"path": "center/router/router_es_index_pattern.go",
"chars": 1904,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v"
},
{
"path": "center/router/router_event_detail.go",
"chars": 4172,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6"
},
{
"path": "center/router/router_event_pipeline.go",
"chars": 16662,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/pipeline/e"
},
{
"path": "center/router/router_funcs.go",
"chars": 4857,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos"
},
{
"path": "center/router/router_heartbeat.go",
"chars": 5027,
"preview": "package router\n\nimport (\n\t\"compress/gzip\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n"
},
{
"path": "center/router/router_login.go",
"chars": 23915,
"preview": "package router\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github."
},
{
"path": "center/router/router_message_template.go",
"chars": 5823,
"preview": "package router\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale"
},
{
"path": "center/router/router_metric_desc.go",
"chars": 2428,
"preview": "package router\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/center/cconf\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ginx\"\n\n\t\""
},
{
"path": "center/router/router_metric_view.go",
"chars": 1581,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/gi"
},
{
"path": "center/router/router_mute.go",
"chars": 6118,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/common\"\n\t\"github.com/cc"
},
{
"path": "center/router/router_mw.go",
"chars": 13131,
"preview": "package router\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/night"
},
{
"path": "center/router/router_notification_record.go",
"chars": 5171,
"preview": "package router\n\nimport (\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/sender\"\n\t\"github.com/ccfos/nightingale/v6/m"
},
{
"path": "center/router/router_notify_channel.go",
"chars": 11428,
"preview": "package router\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/ccf"
},
{
"path": "center/router/router_notify_channel_test.go",
"chars": 334,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestGetFlashDutyChannels(t *testing.T) {\n\t// 构造测试数据\n\tintegrationUrl :"
},
{
"path": "center/router/router_notify_config.go",
"chars": 6328,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.com"
},
{
"path": "center/router/router_notify_rule.go",
"chars": 10746,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/dispatch\"\n\t\"github.com/ccfo"
},
{
"path": "center/router/router_notify_tpl.go",
"chars": 5188,
"preview": "package router\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nighti"
},
{
"path": "center/router/router_opensearch.go",
"chars": 1477,
"preview": "package router\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/datasource/opensearch\"\n\t\"github.com/ccfos/nightingale/v6/dsca"
},
{
"path": "center/router/router_proxy.go",
"chars": 10165,
"preview": "package router\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syn"
},
{
"path": "center/router/router_query.go",
"chars": 6518,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"sync\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/eval\"\n\t\"github.com/ccfos/nighti"
},
{
"path": "center/router/router_recording_rule.go",
"chars": 4076,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/cc"
},
{
"path": "center/router/router_role.go",
"chars": 2409,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/center/cconf\"\n\t\"github.com/ccfos/nigh"
},
{
"path": "center/router/router_role_operation.go",
"chars": 1811,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/center/cconf\"\n\t\"github.com/ccfos/nightingale/v6/"
},
{
"path": "center/router/router_saved_view.go",
"chars": 3306,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/sl"
},
{
"path": "center/router/router_self.go",
"chars": 3379,
"preview": "package router\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/flashduty\"\n\t\"gi"
},
{
"path": "center/router/router_server.go",
"chars": 1300,
"preview": "package router\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ginx\"\n"
},
{
"path": "center/router/router_source_token.go",
"chars": 719,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v"
},
{
"path": "center/router/router_target.go",
"chars": 20374,
"preview": "package router\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightinga"
},
{
"path": "center/router/router_task.go",
"chars": 3532,
"preview": "package router\n\nimport (\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/sender\"\n\t\"github.com/ccfos/nighting"
},
{
"path": "center/router/router_task_tpl.go",
"chars": 6726,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/"
},
{
"path": "center/router/router_tdengine.go",
"chars": 2876,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/ccfos/nightingale/v6/center/cconf\"\n\t\"github.com/ccfos/nighting"
},
{
"path": "center/router/router_trace_logs.go",
"chars": 3487,
"preview": "package router\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\""
},
{
"path": "center/router/router_user.go",
"chars": 12086,
"preview": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nig"
},
{
"path": "center/router/router_user_group.go",
"chars": 5531,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v"
},
{
"path": "center/router/router_user_variable_config.go",
"chars": 2153,
"preview": "package router\n\nimport (\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6"
},
{
"path": "center/sso/init.go",
"chars": 8109,
"preview": "package sso\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/center/cconf\"\n\t\"github.c"
},
{
"path": "center/sso/sync.go",
"chars": 851,
"preview": "package sso\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/ctx\"\n\t\"github.com/toolkits/pkg/logger\"\n)\n\nfunc (s *S"
},
{
"path": "cli/cli.go",
"chars": 150,
"preview": "package cli\n\nimport (\n\t\"github.com/ccfos/nightingale/v6/cli/upgrade\"\n)\n\nfunc Upgrade(configFile string) error {\n\treturn "
},
{
"path": "cli/upgrade/config.go",
"chars": 1150,
"preview": "package upgrade\n\nimport (\n\t\"bytes\"\n\t\"path\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/cfg\"\n\t\"github.com/ccfos/nightingale/v6"
},
{
"path": "cli/upgrade/readme.md",
"chars": 528,
"preview": "# v5 升级 v6 手册\n0. 操作之前,记得备注下数据库!\n\n1. 需要先将你正在使用的夜莺数据源表结构更新到和 v5.15.0 一致,[release](https://github.com/ccfos/nightingale/rel"
},
{
"path": "cli/upgrade/upgrade.go",
"chars": 2507,
"preview": "package upgrade\n\nimport (\n\t\"context\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ct"
},
{
"path": "cli/upgrade/upgrade.sql",
"chars": 4892,
"preview": "use n9e_v5;\n\ninsert into `role_operation`(role_name, operation) values('Guest', '/log/explorer');\ninsert into `role_oper"
},
{
"path": "cmd/alert/main.go",
"chars": 1475,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/ccfos/nightingale/v6/alert\"\n\t\"g"
},
{
"path": "cmd/center/main.go",
"chars": 1520,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/ccfos/nightingale/v6/center\"\n\t\""
},
{
"path": "cmd/cli/main.go",
"chars": 713,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/ccfos/nightingale/v6/cli\"\n\t\"github.com/ccfos/nightingale/v6/pk"
},
{
"path": "cmd/edge/edge.go",
"chars": 3992,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/alert\"\n\t\"github.com/ccfos/nighting"
},
{
"path": "cmd/edge/main.go",
"chars": 1426,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/osx\"\n\t"
},
{
"path": "cmd/pushgw/main.go",
"chars": 1479,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/osx\"\n\t"
},
{
"path": "conf/conf.go",
"chars": 2259,
"preview": "package conf\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/alert/aconf\"\n\t\"github.com/ccfos"
},
{
"path": "conf/crypto.go",
"chars": 1433,
"preview": "package conf\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ccfos/nightingale/v6/pkg/secu\"\n)\n\nfunc decryptConfig(config *ConfigType, cry"
},
{
"path": "cron/clean_notify_record.go",
"chars": 846,
"preview": "package cron\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ctx\"\n\n\t\""
},
{
"path": "cron/clean_pipeline_execution.go",
"chars": 1690,
"preview": "package cron\n\nimport (\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n\t\"github.com/ccfos/nightingale/v6/pkg/ctx\"\n\n\t\""
},
{
"path": "datasource/ck/clickhouse.go",
"chars": 5782,
"preview": "package ck\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/datasource\"\n\tck \"github.com/ccfos/n"
},
{
"path": "datasource/commons/eslike/eslike.go",
"chars": 23096,
"preview": "package eslike\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/araddon/datepars"
},
{
"path": "datasource/datasource.go",
"chars": 4212,
"preview": "package datasource\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ccfos/nightingale/v6/models\"\n)\n\ntype DatasourceT"
},
{
"path": "datasource/doris/doris.go",
"chars": 6710,
"preview": "package doris\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/datasource\"\n\t\"github.com"
},
{
"path": "datasource/es/es.go",
"chars": 10953,
"preview": "package es\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"ti"
},
{
"path": "datasource/mysql/mysql.go",
"chars": 5746,
"preview": "package mysql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/datasource\"\n\t\"github.com"
},
{
"path": "datasource/opensearch/opensearch.go",
"chars": 9662,
"preview": "package opensearch\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n"
},
{
"path": "datasource/postgresql/postgresql.go",
"chars": 9656,
"preview": "package postgresql\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingale/v6/datasourc"
},
{
"path": "datasource/prom/prom.go",
"chars": 653,
"preview": "package prom\n\ntype Prometheus struct {\n\tPrometheusAddr string `json:\"prometheus.addr\"`\n\tPrometheusBasic struct {\n\t\tProm"
},
{
"path": "datasource/tdengine/tdengine.go",
"chars": 11084,
"preview": "package tdengine\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pro"
},
{
"path": "datasource/victorialogs/victorialogs.go",
"chars": 8659,
"preview": "package victorialogs\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/ccfos/nightingal"
},
{
"path": "doc/README.bak.md",
"chars": 7210,
"preview": "<p align=\"center\">\n <a href=\"https://github.com/ccfos/nightingale\">\n <img src=\"doc/img/nightingale_logo_h.png\" alt=\""
},
{
"path": "doc/active-contributors.md",
"chars": 254,
"preview": "## Active Contributors\n\n- [xiaoziv](https://github.com/xiaoziv)\n- [tanxiao1990](https://github.com/tanxiao1990)\n- [bbaob"
},
{
"path": "doc/committers.md",
"chars": 140,
"preview": "## Committers\n\n- [YeningQin](https://github.com/710leo)\n- [FeiKong](https://github.com/kongfei605)\n- [XiaqingDai](https:"
},
{
"path": "doc/community-governance.md",
"chars": 4149,
"preview": "[夜莺监控](https://github.com/ccfos/nightingale \"夜莺监控\")是一款开源云原生监控系统,由滴滴设计开发,2020 年 3 月份开源之后,凭借其优秀的产品设计、灵活性架构和明确清晰的定位,夜莺监控快速发"
},
{
"path": "doc/contributors.md",
"chars": 157,
"preview": "## Contributors\n\n<a href=\"https://github.com/ccfos/nightingale/graphs/contributors\">\n <img src=\"https://contrib.rocks/i"
},
{
"path": "doc/end-users.md",
"chars": 259,
"preview": "## End Users\n\n- [中移动](https://github.com/ccfos/nightingale/issues/897#issuecomment-1086573166)\n- [inke](https://github.c"
},
{
"path": "doc/pmc.md",
"chars": 128,
"preview": "### PMC Chair\n- [laiwei](https://github.com/laiwei)\n\n### PMC Co-Chair\n- [UlricQin](https://github.com/UlricQin)\n\n### PMC"
},
{
"path": "doc/server-dash.json",
"chars": 7802,
"preview": "{\n \"name\": \"夜莺大盘\",\n \"tags\": \"\",\n \"configs\": {\n \"var\": [],\n \"panels\": [\n {\n "
},
{
"path": "docker/.dockerignore",
"chars": 70,
"preview": "compose-host-network\ncompose-postgres\ncompose-bridge\ninitsql\nbuild.sh\n"
},
{
"path": "docker/Dockerfile.goreleaser",
"chars": 199,
"preview": "FROM --platform=$TARGETPLATFORM python:3-slim\n\n\nWORKDIR /app\nADD n9e /app/\nADD etc /app/etc/\nADD integrations /app/integ"
},
{
"path": "docker/Dockerfile.goreleaser.arm64",
"chars": 167,
"preview": "FROM --platform=$TARGETPLATFORM python:3-slim\n\n\nWORKDIR /app\nADD n9e /app/\nADD etc /app/etc/\nADD integrations /app/integ"
},
{
"path": "docker/build.sh",
"chars": 271,
"preview": "#!/bin/sh\nif [ $# -ne 1 ]; then\n\techo \"$0 <tag>\"\n\texit 0\nfi\n\ntag=$1\n\necho \"tag: ${tag}\"\n\nrm -rf n9e pub\ncp ../n9e .\n\ndoc"
},
{
"path": "docker/compose-bridge/docker-compose.yaml",
"chars": 2544,
"preview": "networks:\n nightingale:\n driver: bridge\n\nservices:\n mysql:\n image: \"mysql:8\"\n container_name: mysql\n hostn"
},
{
"path": "docker/compose-bridge/etc-categraf/config.toml",
"chars": 1530,
"preview": "[global]\n# whether print configs\nprint_configs = false\n\n# add label(agent_hostname) to series\n# \"\" -> auto detect hostna"
},
{
"path": "docker/compose-bridge/etc-categraf/input.cpu/cpu.toml",
"chars": 92,
"preview": "# # collect interval\n# interval = 15\n\n# # whether collect per cpu\n# collect_per_cpu = false\n"
},
{
"path": "docker/compose-bridge/etc-categraf/input.disk/disk.toml",
"chars": 337,
"preview": "# # collect interval\n# interval = 15\n\n# # By default stats will be gathered for all mount points.\n# # Set mount_points w"
},
{
"path": "docker/compose-bridge/etc-categraf/input.diskio/diskio.toml",
"chars": 227,
"preview": "# # collect interval\n# interval = 15\n\n# # By default, categraf will gather stats for all devices including disk partitio"
},
{
"path": "docker/compose-bridge/etc-categraf/input.kernel/kernel.toml",
"chars": 37,
"preview": "# # collect interval\n# interval = 15\n"
},
{
"path": "docker/compose-bridge/etc-categraf/input.mem/mem.toml",
"chars": 116,
"preview": "# # collect interval\n# interval = 15\n\n# # whether collect platform specified metrics\ncollect_platform_fields = true\n"
},
{
"path": "docker/compose-bridge/etc-categraf/input.mysql/mysql.toml",
"chars": 1081,
"preview": "[[instances]]\naddress = \"mysql:3306\"\nusername = \"root\"\npassword = \"1234\"\n\n# # set tls=custom to enable tls\n# parameters "
},
{
"path": "docker/compose-bridge/etc-categraf/input.net/net.toml",
"chars": 217,
"preview": "# # collect interval\n# interval = 15\n\n# # whether collect protocol stats on Linux\n# collect_protocol_stats = false\n\n# # "
},
{
"path": "docker/compose-bridge/etc-categraf/input.netstat/netstat.toml",
"chars": 37,
"preview": "# # collect interval\n# interval = 15\n"
},
{
"path": "docker/compose-bridge/etc-categraf/input.processes/processes.toml",
"chars": 143,
"preview": "# # collect interval\n# interval = 15\n\n# # force use ps command to gather\n# force_ps = false\n\n# # force use /proc to gath"
},
{
"path": "docker/compose-bridge/etc-categraf/input.prometheus/prometheus.toml",
"chars": 63,
"preview": "[[instances]]\nurls = [\n \"http://nightingale:17000/metrics\"\n]"
},
{
"path": "docker/compose-bridge/etc-categraf/input.redis/redis.toml",
"chars": 1016,
"preview": "[[instances]]\naddress = \"redis:6379\"\nusername = \"\"\npassword = \"\"\n# pool_size = 2\n\n## 是否开启slowlog 收集\n# gather_slowlog = t"
},
{
"path": "docker/compose-bridge/etc-categraf/input.system/system.toml",
"chars": 111,
"preview": "# # collect interval\n# interval = 15\n\n# # whether collect metric: system_n_users\n# collect_user_number = false\n"
}
]
// ... and 602 more files (download for full content)
About this extraction
This page contains the full source code of the ccfos/nightingale GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 802 files (9.6 MB), approximately 2.6M tokens, and a symbol index with 4509 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.