├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── enhancement.md │ └── question.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── n9e.yml ├── .gitignore ├── .goreleaser.yaml ├── LICENSE ├── Makefile ├── README.md ├── README_en.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 │ ├── pipeline.go │ └── processor │ │ ├── callback │ │ └── callback.go │ │ ├── common │ │ └── common.go │ │ ├── eventdrop │ │ └── event_drop.go │ │ ├── eventupdate │ │ └── event_update.go │ │ └── relabel │ │ └── relabel.go ├── process │ ├── alert_cur_event.go │ └── process.go ├── queue │ └── queue.go ├── record │ ├── prom_rule.go │ ├── sample.go │ └── scheduler.go ├── router │ ├── router.go │ └── router_event.go └── sender │ ├── callback.go │ ├── dingtalk.go │ ├── email.go │ ├── feishu.go │ ├── feishucard.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_his_event.go │ ├── router_alert_rule.go │ ├── router_alert_subscribe.go │ ├── router_board.go │ ├── router_builtin.go │ ├── router_builtin_componet.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_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_proxy.go │ ├── router_query.go │ ├── router_recording_rule.go │ ├── router_role.go │ ├── router_role_operation.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_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 ├── datasource ├── ck │ └── clickhouse.go ├── commons │ └── eslike │ │ └── eslike.go ├── datasource.go ├── es │ └── es.go ├── prom │ └── prom.go └── tdengine │ └── tdengine.go ├── doc ├── README.bak.md ├── active-contributors.md ├── committers.md ├── community-governance.md ├── contributors.md ├── end-users.md ├── img │ ├── Nightingale_L_V.png │ ├── alert-events.png │ ├── arch-product.png │ ├── arch-system.png │ ├── arch.png │ ├── ccf-logo.png │ ├── ccf-n9e.png │ ├── dingtalk.png │ ├── install-vm.png │ ├── intro.gif │ ├── mysql-alerts.png │ ├── n9e-arch-latest.png │ ├── n9e-node-dashboard.png │ ├── n9e-screenshot-gif-v6.gif │ ├── n9e-vx-new.png │ ├── nightingale_logo_h.png │ ├── nightingale_logo_v.png │ ├── readme │ │ ├── 20240221152601.png │ │ ├── 20240222102119.png │ │ ├── 20240513103305.png │ │ ├── 20240513103530.png │ │ ├── 20240513103628.png │ │ ├── 20240513103825.png │ │ ├── 2025-05-23_18-43-37.png │ │ ├── 2025-05-23_18-46-06.png │ │ ├── 2025-05-23_18-49-02.png │ │ ├── 2025-05-30_08-49-28.png │ │ ├── logos.png │ │ └── n9e-switch-i18n.png │ ├── redis-dash.png │ ├── vm-cluster-arch.png │ ├── wecom.png │ └── wx.jpg ├── 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-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-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-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 ├── sqlbase │ ├── base.go │ ├── timeseries.go │ └── timeseries_test.go ├── tdengine │ └── tdengine.go └── types │ ├── timeseries.go │ └── types.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 ├── front └── statik │ └── statik.go ├── go.mod ├── go.sum ├── integrations ├── AMD_ROCm_SMI │ ├── collect │ │ └── amd_rocm_smi │ │ │ └── rocm.toml │ ├── icon │ │ └── rocm_smi.png │ └── 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 │ ├── icon │ │ └── aliyun.png │ └── markdown │ │ ├── README.md │ │ ├── ecs.png │ │ ├── rds.png │ │ ├── redis.png │ │ ├── slb.png │ │ └── waf.png ├── AppDynamics │ ├── collect │ │ └── appdynamics │ │ │ └── app.toml │ ├── icon │ │ └── appdynamics.png │ └── 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 │ ├── icon │ │ └── automq.png │ ├── markdown │ │ └── overview.md │ └── metrics │ │ └── exporter.json ├── Bind │ ├── collect │ │ └── bind │ │ │ └── bind.toml │ ├── icon │ │ └── bind.png │ └── markdown │ │ └── README.md ├── Canal │ ├── dashboards │ │ └── canal_by_categraf.json │ ├── icon │ │ └── canal.png │ └── markdown │ │ └── README.md ├── Ceph │ ├── alerts │ │ └── ceph_by_categraf.json │ ├── dashboards │ │ └── ceph_by_categraf.json │ ├── icon │ │ └── ceph.png │ └── markdown │ │ ├── README.md │ │ ├── alerts.png │ │ ├── ceph-alerts.png │ │ ├── ceph-dash.png │ │ └── ceph.png ├── ClickHouse │ ├── alerts │ │ ├── clickhouse_by_categraf.json │ │ └── clickhouse_by_exporter.json │ ├── collect │ │ └── clickhouse │ │ │ └── clickhouse.toml │ ├── dashboards │ │ ├── clickhouse_by_categraf.json │ │ └── clickhouse_by_exporter.json │ ├── icon │ │ └── clickhouse.png │ ├── markdown │ │ └── README.md │ └── metrics │ │ ├── clickhouse_by_categraf.json │ │ └── clickhouse_by_exporter.json ├── CloudWatch │ ├── collect │ │ └── cloudwatch │ │ │ └── cloud.toml │ ├── dashboards │ │ └── dashboard-by-aws-rds.json │ ├── icon │ │ └── cloudwatch.png │ └── markdown │ │ └── README.md ├── Consul │ ├── collect │ │ └── consul │ │ │ └── consul.toml │ ├── icon │ │ └── consul.png │ └── markdown │ │ └── README.md ├── Dns_Query │ ├── collect │ │ └── dns_query │ │ │ └── dns_query.toml │ ├── icon │ │ └── dns.png │ └── markdown │ │ └── README.md ├── Docker │ ├── collect │ │ └── docker │ │ │ └── docker.toml │ ├── icon │ │ └── docker.png │ └── markdown │ │ └── README.md ├── Doris │ ├── alerts │ │ └── doris_by_categraf.json │ ├── collect │ │ └── prometheus │ │ │ └── collect_doris_examples.toml │ ├── dashboards │ │ └── Doris_Overview.json │ ├── icon │ │ └── doris.svg │ └── 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_a.json │ │ ├── elasticsearch_by_categraf_b.json │ │ └── elasticsearch_by_exporter.json │ ├── icon │ │ └── elasticsearch.png │ ├── markdown │ │ ├── README.md │ │ └── es-dashboard.jpeg │ └── metrics │ │ └── categraf-base.json ├── Exec │ ├── collect │ │ └── exec │ │ │ └── exec.toml │ ├── icon │ │ └── exec.png │ └── markdown │ │ └── README.md ├── Filecount │ ├── collect │ │ └── filecount │ │ │ └── filecount.toml │ ├── icon │ │ └── filecount.png │ └── markdown │ │ └── README.md ├── Gitlab │ ├── alerts │ │ └── gitlab_by_categraf.json │ ├── dashboards │ │ ├── MachinePerformance.json │ │ ├── NGINXVTS.json │ │ ├── Overview.json │ │ ├── PostgreSQL.json │ │ └── Redis.json │ ├── icon │ │ └── gitlab.png │ └── markdown │ │ └── README.md ├── GoogleCloud │ ├── collect │ │ └── googlecloud │ │ │ └── gcp.toml │ ├── icon │ │ └── gcp.png │ └── markdown │ │ └── README.md ├── HAProxy │ ├── collect │ │ └── haproxy │ │ │ └── haproxy.toml │ ├── dashboards │ │ └── dashboard.json │ ├── icon │ │ └── haproxy.png │ └── markdown │ │ └── README.md ├── HTTP_Response │ ├── alerts │ │ └── http_response_by_categraf.json │ ├── collect │ │ └── http_response │ │ │ └── http_response.toml │ ├── dashboards │ │ └── http_response_by_categraf.json │ ├── icon │ │ └── http_response.png │ ├── 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 │ ├── icon │ │ └── ipmi.png │ └── markdown │ │ └── README.md ├── IPVS │ ├── collect │ │ └── ipvs │ │ │ └── ipvs.toml │ ├── icon │ │ └── ipvs.png │ └── markdown │ │ └── README.md ├── JMX │ ├── dashboards │ │ └── jmx_by_exporter.json │ └── icon │ │ └── jmx.png ├── Jenkins │ ├── collect │ │ └── jenkins │ │ │ └── jenkins.toml │ ├── icon │ │ └── jenkins.png │ └── 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 │ ├── icon │ │ └── jolokia.png │ └── 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 │ ├── icon │ │ └── kafka.png │ ├── markdown │ │ ├── README.md │ │ ├── alerts-kafka.png │ │ ├── alerts..png │ │ ├── dash-kafka.png │ │ └── dashboards.png │ └── 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 │ ├── icon │ │ └── kubernetes.png │ ├── markdown │ │ └── README.md │ ├── metrics │ │ ├── k8s-node.json │ │ └── k8s-pod.json │ └── record-rules │ │ ├── kube-controller-plane.json │ │ └── node-exporer.json ├── Ldap │ ├── collect │ │ └── ldap │ │ │ └── ldap.toml │ ├── icon │ │ └── ldap.png │ └── 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 │ │ └── exporter-detail.json │ ├── icon │ │ └── linux.png │ ├── markdown │ │ └── README.md │ └── metrics │ │ ├── categraf-base.json │ │ └── exporter-base.json ├── Logstash │ ├── collect │ │ └── logstash │ │ │ └── logstash.toml │ ├── dashboards │ │ └── logstash-dash.json │ ├── icon │ │ └── logstash.png │ └── markdown │ │ └── README.md ├── MinIO │ ├── alerts │ │ └── minio_by_categraf.json │ ├── dashboards │ │ └── minio_by_categraf.json │ ├── icon │ │ └── minio.png │ └── markdown │ │ ├── README.md │ │ ├── alerts-minio.png │ │ ├── alerts.png │ │ ├── dash-minio.png │ │ └── minio.png ├── MongoDB │ ├── alerts │ │ └── mongo_by_exporter.json │ ├── collect │ │ └── mongodb │ │ │ └── mongodb.toml │ ├── dashboards │ │ └── mongo_by_exporter.json │ ├── icon │ │ └── mongodb.png │ └── markdown │ │ └── README.md ├── Mtail │ ├── collect │ │ └── mtail │ │ │ └── mtail.toml │ ├── icon │ │ └── mtail.png │ └── markdown │ │ ├── README.md │ │ ├── timestamp.png │ │ └── timezone.png ├── 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 │ ├── icon │ │ └── mysql.png │ ├── markdown │ │ ├── README.md │ │ └── mysql.md │ └── metrics │ │ └── categraf-base.json ├── N9E │ ├── dashboards │ │ ├── n9e_server.json │ │ ├── n9e_v6.json │ │ └── n9e_v8.json │ ├── icon │ │ └── nightingale.png │ └── markdown │ │ └── README.md ├── NFSClient │ ├── collect │ │ └── nfsclient │ │ │ └── nfsclient.toml │ ├── icon │ │ └── nfsclient.png │ └── markdown │ │ └── README.md ├── NSQ │ ├── collect │ │ └── nsq │ │ │ └── nsq.toml │ ├── icon │ │ └── nsq.png │ └── markdown │ │ └── README.md ├── NVIDIA │ ├── collect │ │ └── nvidia_smi │ │ │ └── nvidia_smi.toml │ ├── dashboards │ │ └── nvidia-gpu-metrics-by-categraf.json │ ├── icon │ │ └── nvidia.png │ └── 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 │ ├── icon │ │ └── net_response.png │ ├── markdown │ │ └── README.md │ └── metrics │ │ └── categraf.json ├── Netstat_Filter │ ├── collect │ │ └── netstat_filter │ │ │ └── netstat_filter.toml │ ├── icon │ │ └── netstat_filter.png │ └── 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 │ ├── icon │ │ └── nginx.png │ ├── markdown │ │ └── README.md │ └── metrics │ │ └── categraf.json ├── Oracle │ ├── alerts │ │ └── oracle_alert.json │ ├── collect │ │ └── oracle │ │ │ └── oracle.toml │ ├── dashboards │ │ └── oracle_by_categraf.json │ ├── icon │ │ └── oracle.png │ └── markdown │ │ └── README.md ├── PHP │ ├── collect │ │ └── phpfpm │ │ │ └── phpfpm.toml │ ├── icon │ │ └── phpfpm.png │ └── markdown │ │ └── README.md ├── Ping │ ├── alerts │ │ └── ping_by_categraf.json │ ├── collect │ │ └── ping │ │ │ └── ping.toml │ ├── dashboards │ │ ├── ping_by_categraf_a.json │ │ └── ping_by_categraf_b.json │ ├── icon │ │ └── ping.png │ ├── markdown │ │ └── README.md │ └── metrics │ │ └── categraf.json ├── PostgreSQL │ ├── alerts │ │ └── postgresql_by_categraf.json │ ├── collect │ │ └── postgresql │ │ │ └── postgresql.toml │ ├── dashboards │ │ └── postgresql_by_categraf.json │ ├── icon │ │ └── postgresql.png │ └── markdown │ │ ├── README.md │ │ ├── alerts-pg.png │ │ ├── alerts.png │ │ ├── dash-pg.png │ │ └── postgresql.png ├── Procstat │ ├── alerts │ │ └── categraf-procstat.json │ ├── collect │ │ └── procstat │ │ │ └── procstat.toml │ ├── dashboards │ │ └── categraf-procstat.json │ ├── icon │ │ └── process.png │ ├── markdown │ │ └── readme.md │ └── metrics │ │ └── categraf.json ├── Prometheus │ ├── collect │ │ └── prometheus │ │ │ └── prometheus.toml │ ├── icon │ │ └── prometheus.png │ └── 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 │ ├── icon │ │ └── rabbitmq.png │ └── markdown │ │ ├── README.md │ │ └── rabbitmq.png ├── 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 │ ├── icon │ │ └── redis.png │ └── markdown │ │ └── README.md ├── SMART │ ├── collect │ │ └── smart │ │ │ └── smart.toml │ ├── dashboards │ │ └── smart.json │ ├── icon │ │ └── smart.png │ └── markdown │ │ └── README.md ├── SNMP │ ├── collect │ │ └── snmp │ │ │ ├── Cisco.toml │ │ │ ├── snmp.toml │ │ │ └── snmp.toml.example │ ├── dashboards │ │ ├── dashboards.json │ │ ├── switch branch.json │ │ └── switch main.json │ ├── icon │ │ └── snmp.png │ └── markdown │ │ └── README.md ├── SQLServer │ ├── collect │ │ └── sqlserver │ │ │ └── sqlserver.toml │ ├── dashboards │ │ └── sqlserver.json │ ├── icon │ │ └── sqlserver.png │ └── markdown │ │ └── README.md ├── SpringBoot │ ├── alerts │ │ └── alerts.json │ ├── dashboards │ │ ├── JVM(Actuator)withapplicationname.json │ │ └── JVM.json │ ├── icon │ │ └── springboot.png │ └── markdown │ │ ├── README.md │ │ ├── actuator.jpeg │ │ └── actuator_2.0.png ├── Switch_Legacy │ ├── collect │ │ └── switch_legacy │ │ │ └── switch_legacy.toml │ ├── dashboards │ │ └── dashboard.json │ ├── icon │ │ └── switch_legacy.png │ └── markdown │ │ └── README.md ├── Systemd │ ├── collect │ │ └── systemd │ │ │ └── systemd.toml │ ├── icon │ │ └── systemd.png │ └── markdown │ │ └── README.md ├── TDEngine │ ├── dashboards │ │ └── tasokeeper3.x.json │ ├── icon │ │ └── tdengine.png │ └── markdown │ │ └── README.md ├── TiDB │ ├── alerts │ │ └── tidb-alerts.json │ ├── dashboards │ │ └── tidb-dashboard.json │ └── icon │ │ └── tidb.png ├── Tomcat │ ├── collect │ │ └── tomcat │ │ │ └── tomcat.toml │ ├── dashboards │ │ └── tomcat_by_categraf.json │ ├── icon │ │ └── tomcat.png │ └── markdown │ │ └── README.md ├── VictoriaMetrics │ ├── alerts │ │ └── alerts.json │ ├── dashboards │ │ ├── victoriametrics-cluster.json │ │ └── victoriametrics-single.json │ ├── icon │ │ └── VictoriaMetrics.png │ └── markdown │ │ ├── README.md │ │ ├── alerts-vm.png │ │ ├── alerts.png │ │ ├── dash-vm.png │ │ └── dashboard.png ├── Whois │ ├── collect │ │ └── whois │ │ │ └── whois.toml │ ├── icon │ │ └── whois.png │ └── markdown │ │ └── README.md ├── Windows │ ├── alerts │ │ ├── windows_by_categraf.json │ │ └── windows_by_exporter.json │ ├── dashboards │ │ ├── windows_by_categraf.json │ │ └── windows_by_exporter.json │ ├── icon │ │ └── windows.png │ └── markdown │ │ ├── README.md │ │ └── windows.png ├── XSKYApi │ ├── collect │ │ └── xskyapi │ │ │ └── xskyapi.toml │ ├── icon │ │ └── xsky.png │ └── markdown │ │ └── README.md ├── ZooKeeper │ ├── alerts │ │ └── zookeeper_by_exporter.json │ ├── collect │ │ └── zookeeper │ │ │ └── zookeeper.toml │ ├── dashboards │ │ └── zookeeper_by_exporter.json │ ├── icon │ │ └── zookeeper.png │ └── markdown │ │ └── README.md ├── cAdvisor │ ├── collect │ │ └── cadvisor │ │ │ └── cadvisor.toml │ ├── dashboards │ │ └── dashboard.json │ ├── icon │ │ └── cadvisor.png │ ├── markdown │ │ └── README.md │ └── metrics │ │ └── exporter-base.json └── vSphere │ ├── alerts │ └── alerts.json │ ├── collect │ └── vsphere │ │ └── vsphere.toml │ ├── dashboards │ ├── vmware_by_vsphere-monitor.json │ └── vsphere.json │ ├── icon │ └── vsphere.png │ └── 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_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 ├── 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 ├── pkg ├── aop │ ├── log.go │ └── rec.go ├── cas │ └── cas.go ├── cfg │ ├── cfg.go │ └── scan.go ├── choice │ └── choice.go ├── ctx │ └── ctx.go ├── fasttime │ └── fasttime.go ├── flashduty │ ├── post.go │ ├── sync_user.go │ └── sync_user_group.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 ├── 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 │ ├── json │ └── json-iterator.go │ ├── kafka_writer.go │ ├── queue.go │ ├── relabel.go │ ├── relabel_test.go │ └── writer.go └── storage ├── redis.go ├── redis_test.go └── storage.go /.gitattributes: -------------------------------------------------------------------------------- 1 | *.css linguist-language=go 2 | *.less linguist-language=go 3 | *.js linguist-language=go 4 | *.tsx linguist-language=go 5 | *.html linguist-language=go 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Nightingale docs 4 | url: https://n9e.github.io/ 5 | about: You may want to read through the document before asking questions. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an enhancement to the nightingale project 4 | labels: kind/feature 5 | 6 | --- 7 | 8 | 9 | **What would you like to be added**: 10 | 11 | **Why is this needed**: -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report & Usage Question 2 | description: Reporting a bug or asking a question about how to use Nightingale 3 | labels: [] 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | The more detailed the form is filled in, the easier the problem will be solved. 10 | 提供的信息越详细,问题解决的可能性就越大。另外, 提问之前请先搜索历史 issue (包括 close 的), 以免重复提问。 11 | - type: textarea 12 | id: question 13 | attributes: 14 | label: Question and Steps to reproduce 15 | description: Describe your question and steps to reproduce the bug. 描述问题以及复现步骤 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: logs 20 | attributes: 21 | label: Relevant logs and configurations 22 | description: Relevant logs and configurations. 报错日志([查看方法](https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v6/faq/how-to-check-logs/))以及各个相关组件的配置信息 23 | render: text 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: system-info 28 | attributes: 29 | label: Version 30 | description: Include nightingale version, operating system, and other relevant details. 请告知夜莺的版本、操作系统的版本、CPU架构等信息 31 | validations: 32 | required: true 33 | 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **What type of PR is this?** 2 | 3 | **What this PR does / why we need it**: 4 | 7 | 8 | **Which issue(s) this PR fixes**: 9 | 12 | Fixes # 13 | 14 | **Special notes for your reviewer**: -------------------------------------------------------------------------------- /.github/workflows/n9e.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | env: 8 | GO_VERSION: 1.23 9 | 10 | jobs: 11 | goreleaser: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout Source Code 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | - name: Setup Go Environment 19 | uses: actions/setup-go@v3 20 | with: 21 | go-version: ${{ env.GO_VERSION }} 22 | - uses: docker/login-action@v2 23 | with: 24 | username: ${{ secrets.DOCKERHUB_USERNAME }} 25 | password: ${{ secrets.DOCKERHUB_TOKEN }} 26 | - name: Run GoReleaser 27 | uses: goreleaser/goreleaser-action@v3 28 | with: 29 | distribution: goreleaser 30 | version: '~> v1' 31 | args: release --rm-dist 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.exe~ 3 | *.dll 4 | *.dylib 5 | *.test 6 | *.out 7 | *.prof 8 | *.log 9 | *.o 10 | *.a 11 | *.so 12 | *.db 13 | *.sw[po] 14 | *.tar.gz 15 | *.[568vq] 16 | [568vq].out 17 | 18 | *.cgo1.go 19 | *.cgo2.c 20 | _cgo_defun.c 21 | _cgo_gotypes.go 22 | _cgo_export.* 23 | _testmain.go 24 | _obj 25 | _test 26 | 27 | /log* 28 | /bin 29 | /out 30 | /build 31 | /dist 32 | /etc/*.local.yml 33 | /etc/*.local.conf 34 | /etc/rsa/* 35 | /etc/plugins/*.local.yml 36 | /etc/script/rules.yaml 37 | /etc/script/alert-rules.json 38 | /etc/script/record-rules.json 39 | /data* 40 | /tarball 41 | /run 42 | /vendor 43 | /tmp 44 | /pub 45 | /n9e 46 | /docker/pub 47 | /docker/n9e 48 | /docker/compose-bridge/mysqldata 49 | /docker/compose-host-network/mysqldata 50 | /docker/compose-host-network-metric-log/mysqldata 51 | /docker/compose-host-network-metric-log/n9e-logs 52 | /docker/compose-postgres/pgdata 53 | /etc.local* 54 | /front/statik/statik.go 55 | /docker/compose-bridge/etc-nightingale/rsa/ 56 | 57 | .alerts 58 | .idea 59 | .index 60 | .vscode 61 | .DS_Store 62 | .cache-loader 63 | .payload 64 | queries.active 65 | 66 | /n9e-* 67 | n9e.sql 68 | 69 | !/datasource 70 | 71 | .env.json -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: prebuild build 2 | 3 | ROOT:=$(shell pwd -P) 4 | GIT_COMMIT:=$(shell git --work-tree ${ROOT} rev-parse 'HEAD^{commit}') 5 | _GIT_VERSION:=$(shell git --work-tree ${ROOT} describe --tags --abbrev=14 "${GIT_COMMIT}^{commit}" 2>/dev/null) 6 | TAG=$(shell echo "${_GIT_VERSION}" | awk -F"-" '{print $$1}') 7 | RELEASE_VERSION:="$(TAG)-$(GIT_COMMIT)" 8 | 9 | all: prebuild build 10 | 11 | prebuild: 12 | echo "begin download and embed the front-end file..." 13 | sh fe.sh 14 | echo "front-end file download and embedding completed." 15 | 16 | build: 17 | go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e ./cmd/center/main.go 18 | 19 | build-edge: 20 | go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-edge ./cmd/edge/ 21 | 22 | build-alert: 23 | go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-alert ./cmd/alert/main.go 24 | 25 | build-pushgw: 26 | go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-pushgw ./cmd/pushgw/main.go 27 | 28 | build-cli: 29 | go build -ldflags "-w -s -X github.com/ccfos/nightingale/v6/pkg/version.Version=$(RELEASE_VERSION)" -o n9e-cli ./cmd/cli/main.go 30 | 31 | run: 32 | nohup ./n9e > n9e.log 2>&1 & 33 | 34 | run-alert: 35 | nohup ./n9e-alert > n9e-alert.log 2>&1 & 36 | 37 | run-pushgw: 38 | nohup ./n9e-pushgw > n9e-pushgw.log 2>&1 & 39 | 40 | release: 41 | goreleaser --skip-validate --skip-publish --snapshot -------------------------------------------------------------------------------- /alert/aconf/conf.go: -------------------------------------------------------------------------------- 1 | package aconf 2 | 3 | import ( 4 | "path" 5 | ) 6 | 7 | type Alert struct { 8 | Disable bool 9 | EngineDelay int64 10 | Heartbeat HeartbeatConfig 11 | Alerting Alerting 12 | } 13 | 14 | type SMTPConfig struct { 15 | Host string 16 | Port int 17 | User string 18 | Pass string 19 | From string 20 | InsecureSkipVerify bool 21 | Batch int 22 | } 23 | 24 | type HeartbeatConfig struct { 25 | IP string 26 | Interval int64 27 | Endpoint string 28 | EngineName string 29 | } 30 | 31 | type Alerting struct { 32 | Timeout int64 33 | TemplatesDir string 34 | NotifyConcurrency int 35 | WebhookBatchSend bool 36 | } 37 | 38 | type CallPlugin struct { 39 | Enable bool 40 | PluginPath string 41 | Caller string 42 | } 43 | 44 | type RedisPub struct { 45 | Enable bool 46 | ChannelPrefix string 47 | ChannelKey string 48 | } 49 | 50 | func (a *Alert) PreCheck(configDir string) { 51 | if a.Alerting.TemplatesDir == "" { 52 | a.Alerting.TemplatesDir = path.Join(configDir, "template") 53 | } 54 | 55 | if a.Alerting.NotifyConcurrency == 0 { 56 | a.Alerting.NotifyConcurrency = 10 57 | } 58 | 59 | if a.Heartbeat.Interval == 0 { 60 | a.Heartbeat.Interval = 1000 61 | } 62 | 63 | if a.EngineDelay == 0 { 64 | a.EngineDelay = 30 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /alert/common/key.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/ccfos/nightingale/v6/models" 8 | ) 9 | 10 | func RuleKey(datasourceId, id int64) string { 11 | return fmt.Sprintf("alert-%d-%d", datasourceId, id) 12 | } 13 | 14 | func MatchTags(eventTagsMap map[string]string, itags []models.TagFilter) bool { 15 | for _, filter := range itags { 16 | value, has := eventTagsMap[filter.Key] 17 | if !has { 18 | return false 19 | } 20 | if !matchTag(value, filter) { 21 | return false 22 | } 23 | } 24 | return true 25 | } 26 | func MatchGroupsName(groupName string, groupFilter []models.TagFilter) bool { 27 | for _, filter := range groupFilter { 28 | if !matchTag(groupName, filter) { 29 | return false 30 | } 31 | } 32 | return true 33 | } 34 | 35 | func matchTag(value string, filter models.TagFilter) bool { 36 | switch filter.Func { 37 | case "==": 38 | return strings.TrimSpace(filter.Value) == strings.TrimSpace(value) 39 | case "!=": 40 | return strings.TrimSpace(filter.Value) != strings.TrimSpace(value) 41 | case "in": 42 | _, has := filter.Vset[value] 43 | return has 44 | case "not in": 45 | _, has := filter.Vset[value] 46 | return !has 47 | case "=~": 48 | return filter.Regexp.MatchString(value) 49 | case "!~": 50 | return !filter.Regexp.MatchString(value) 51 | } 52 | // unexpect func 53 | return false 54 | } 55 | -------------------------------------------------------------------------------- /alert/dispatch/log.go: -------------------------------------------------------------------------------- 1 | package dispatch 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/models" 5 | 6 | "github.com/toolkits/pkg/logger" 7 | ) 8 | 9 | func LogEvent(event *models.AlertCurEvent, location string, err ...error) { 10 | status := "triggered" 11 | if event.IsRecovered { 12 | status = "recovered" 13 | } 14 | 15 | message := "" 16 | if len(err) > 0 && err[0] != nil { 17 | message = "error_message: " + err[0].Error() 18 | } 19 | 20 | logger.Infof( 21 | "event(%s %s) %s: rule_id=%d sub_id:%d notify_rule_ids:%v cluster:%s %v%s@%d %s", 22 | event.Hash, 23 | status, 24 | location, 25 | event.RuleId, 26 | event.SubRuleId, 27 | event.NotifyRuleIds, 28 | event.Cluster, 29 | event.TagsJSON, 30 | event.TriggerValue, 31 | event.TriggerTime, 32 | message, 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /alert/dispatch/notify_channel.go: -------------------------------------------------------------------------------- 1 | package dispatch 2 | 3 | // NotifyChannels channelKey -> bool 4 | type NotifyChannels map[string]bool 5 | 6 | func NewNotifyChannels(channels []string) NotifyChannels { 7 | nc := make(NotifyChannels) 8 | for _, ch := range channels { 9 | nc[ch] = true 10 | } 11 | return nc 12 | } 13 | 14 | func (nc NotifyChannels) OrMerge(other NotifyChannels) { 15 | nc.merge(other, func(a, b bool) bool { return a || b }) 16 | } 17 | 18 | func (nc NotifyChannels) AndMerge(other NotifyChannels) { 19 | nc.merge(other, func(a, b bool) bool { return a && b }) 20 | } 21 | 22 | func (nc NotifyChannels) merge(other NotifyChannels, f func(bool, bool) bool) { 23 | if other == nil { 24 | return 25 | } 26 | for k, v := range other { 27 | if curV, has := nc[k]; has { 28 | nc[k] = f(curV, v) 29 | } else { 30 | nc[k] = v 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /alert/naming/leader.go: -------------------------------------------------------------------------------- 1 | package naming 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/toolkits/pkg/logger" 7 | ) 8 | 9 | func (n *Naming) IamLeader() bool { 10 | if !n.ctx.IsCenter { 11 | return false 12 | } 13 | 14 | servers, err := n.ActiveServersByEngineName() 15 | if err != nil { 16 | logger.Errorf("failed to get active servers: %v", err) 17 | return false 18 | } 19 | 20 | if len(servers) == 0 { 21 | logger.Errorf("active servers empty") 22 | return false 23 | } 24 | 25 | sort.Strings(servers) 26 | 27 | return n.heartbeatConfig.Endpoint == servers[0] 28 | } 29 | -------------------------------------------------------------------------------- /alert/pipeline/pipeline.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | _ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/callback" 5 | _ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/eventdrop" 6 | _ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/eventupdate" 7 | _ "github.com/ccfos/nightingale/v6/alert/pipeline/processor/relabel" 8 | ) 9 | 10 | func Init() { 11 | } 12 | -------------------------------------------------------------------------------- /alert/pipeline/processor/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // InitProcessor 是一个通用的初始化处理器的方法 8 | // 使用泛型简化处理器初始化逻辑 9 | // T 必须是 models.Processor 接口的实现 10 | func InitProcessor[T any](settings interface{}) (T, error) { 11 | var zero T 12 | b, err := json.Marshal(settings) 13 | if err != nil { 14 | return zero, err 15 | } 16 | 17 | var result T 18 | err = json.Unmarshal(b, &result) 19 | if err != nil { 20 | return zero, err 21 | } 22 | 23 | return result, nil 24 | } 25 | -------------------------------------------------------------------------------- /alert/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ccfos/nightingale/v6/alert/astats" 7 | "github.com/toolkits/pkg/container/list" 8 | ) 9 | 10 | var EventQueue = list.NewSafeListLimited(10000000) 11 | 12 | func ReportQueueSize(stats *astats.Stats) { 13 | for { 14 | time.Sleep(time.Second) 15 | 16 | stats.GaugeAlertQueueSize.Set(float64(EventQueue.Len())) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /alert/sender/plugin_cmd_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package sender 5 | 6 | import ( 7 | "os/exec" 8 | "syscall" 9 | ) 10 | 11 | func startCmd(c *exec.Cmd) error { 12 | c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 13 | return c.Start() 14 | } 15 | -------------------------------------------------------------------------------- /alert/sender/plugin_cmd_windows.go: -------------------------------------------------------------------------------- 1 | package sender 2 | 3 | import "os/exec" 4 | 5 | func startCmd(c *exec.Cmd) error { 6 | return c.Start() 7 | } 8 | -------------------------------------------------------------------------------- /center/cconf/conf.go: -------------------------------------------------------------------------------- 1 | package cconf 2 | 3 | import "time" 4 | 5 | type Center struct { 6 | Plugins []Plugin 7 | MetricsYamlFile string 8 | OpsYamlFile string 9 | BuiltinIntegrationsDir string 10 | I18NHeaderKey string 11 | MetricDesc MetricDescType 12 | AnonymousAccess AnonymousAccess 13 | UseFileAssets bool 14 | FlashDuty FlashDuty 15 | EventHistoryGroupView bool 16 | CleanNotifyRecordDay int 17 | MigrateBusiGroupLabel bool 18 | } 19 | 20 | type Plugin struct { 21 | Id int64 `json:"id"` 22 | Category string `json:"category"` 23 | Type string `json:"plugin_type"` 24 | TypeName string `json:"plugin_type_name"` 25 | } 26 | 27 | type FlashDuty struct { 28 | Api string 29 | Headers map[string]string 30 | Timeout time.Duration 31 | } 32 | 33 | type AnonymousAccess struct { 34 | PromQuerier bool 35 | AlertDetail bool 36 | } 37 | 38 | func (c *Center) PreCheck() { 39 | if len(c.Plugins) == 0 { 40 | c.Plugins = Plugins 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /center/cconf/event_example.go: -------------------------------------------------------------------------------- 1 | package cconf 2 | 3 | const EVENT_EXAMPLE = ` 4 | { 5 | "id": 1000000, 6 | "cate": "prometheus", 7 | "datasource_id": 1, 8 | "group_id": 1, 9 | "group_name": "Default Busi Group", 10 | "hash": "2cb966f9ba1cdc7af94c3796e855955a", 11 | "rule_id": 23, 12 | "rule_name": "测试告警", 13 | "rule_note": "测试告警", 14 | "rule_prod": "metric", 15 | "rule_config": { 16 | "queries": [ 17 | { 18 | "key": "all_hosts", 19 | "op": "==", 20 | "values": [] 21 | } 22 | ], 23 | "triggers": [ 24 | { 25 | "duration": 3, 26 | "percent": 10, 27 | "severity": 3, 28 | "type": "pct_target_miss" 29 | } 30 | ] 31 | }, 32 | "prom_for_duration": 60, 33 | "prom_eval_interval": 30, 34 | "callbacks": ["https://n9e.github.io"], 35 | "notify_recovered": 1, 36 | "notify_channels": ["dingtalk"], 37 | "notify_groups": [], 38 | "notify_groups_obj": null, 39 | "target_ident": "host01", 40 | "target_note": "机器备注", 41 | "trigger_time": 1677229517, 42 | "trigger_value": "2273533952", 43 | "tags": [ 44 | "__name__=disk_free", 45 | "dc=qcloud-dev", 46 | "device=vda1", 47 | "fstype=ext4", 48 | "ident=tt-fc-dev00.nj" 49 | ], 50 | "is_recovered": false, 51 | "notify_users_obj": null, 52 | "last_eval_time": 1677229517, 53 | "last_sent_time": 1677229517, 54 | "notify_cur_number": 1, 55 | "first_trigger_time": 1677229517, 56 | "annotations": { 57 | "summary": "测试告警" 58 | } 59 | } 60 | ` 61 | -------------------------------------------------------------------------------- /center/cconf/metric.go: -------------------------------------------------------------------------------- 1 | package cconf 2 | 3 | import ( 4 | "path" 5 | 6 | "github.com/toolkits/pkg/file" 7 | ) 8 | 9 | // metricDesc , As load map happens before read map, there is no necessary to use concurrent map for metric desc store 10 | type MetricDescType struct { 11 | CommonDesc map[string]string `yaml:",inline" json:"common"` 12 | Zh map[string]string `yaml:"zh" json:"zh"` 13 | En map[string]string `yaml:"en" json:"en"` 14 | } 15 | 16 | var MetricDesc MetricDescType 17 | 18 | // GetMetricDesc , if metric is not registered, empty string will be returned 19 | func GetMetricDesc(lang, metric string) string { 20 | var m map[string]string 21 | 22 | switch lang { 23 | case "en": 24 | m = MetricDesc.En 25 | default: 26 | m = MetricDesc.Zh 27 | } 28 | 29 | if m != nil { 30 | if desc, ok := m[metric]; ok { 31 | return desc 32 | } 33 | } 34 | 35 | if MetricDesc.CommonDesc != nil { 36 | if desc, ok := MetricDesc.CommonDesc[metric]; ok { 37 | return desc 38 | } 39 | } 40 | 41 | return "" 42 | } 43 | func LoadMetricsYaml(configDir, metricsYamlFile string) error { 44 | fp := metricsYamlFile 45 | if fp == "" { 46 | fp = path.Join(configDir, "metrics.yaml") 47 | } 48 | if !file.IsExist(fp) { 49 | return nil 50 | } 51 | return file.ReadYaml(fp, &MetricDesc) 52 | } 53 | -------------------------------------------------------------------------------- /center/cconf/plugin.go: -------------------------------------------------------------------------------- 1 | package cconf 2 | 3 | var Plugins = []Plugin{ 4 | { 5 | Id: 1, 6 | Category: "timeseries", 7 | Type: "prometheus", 8 | TypeName: "Prometheus Like", 9 | }, 10 | { 11 | Id: 2, 12 | Category: "logging", 13 | Type: "elasticsearch", 14 | TypeName: "Elasticsearch", 15 | }, 16 | { 17 | Id: 3, 18 | Category: "loki", 19 | Type: "loki", 20 | TypeName: "Loki", 21 | }, 22 | { 23 | Id: 4, 24 | Category: "timeseries", 25 | Type: "tdengine", 26 | TypeName: "TDengine", 27 | }, 28 | { 29 | Id: 5, 30 | Category: "logging", 31 | Type: "ck", 32 | TypeName: "ClickHouse", 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /center/cstats/stats.go: -------------------------------------------------------------------------------- 1 | package cstats 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | ) 8 | 9 | const ( 10 | namespace = "n9e" 11 | subsystem = "center" 12 | ) 13 | 14 | var ( 15 | uptime = prometheus.NewCounter( 16 | prometheus.CounterOpts{ 17 | Namespace: namespace, 18 | Subsystem: subsystem, 19 | Name: "uptime", 20 | Help: "HTTP service uptime.", 21 | }, 22 | ) 23 | 24 | RequestDuration = prometheus.NewHistogramVec( 25 | prometheus.HistogramOpts{ 26 | Namespace: namespace, 27 | Subsystem: subsystem, 28 | Buckets: prometheus.DefBuckets, 29 | Name: "http_request_duration_seconds", 30 | Help: "HTTP request latencies in seconds.", 31 | }, []string{"code", "path", "method"}, 32 | ) 33 | 34 | RedisOperationLatency = prometheus.NewHistogramVec( 35 | prometheus.HistogramOpts{ 36 | Namespace: namespace, 37 | Subsystem: subsystem, 38 | Name: "redis_operation_latency_seconds", 39 | Help: "Histogram of latencies for Redis operations", 40 | Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5}, 41 | }, 42 | []string{"operation", "status"}, 43 | ) 44 | ) 45 | 46 | func init() { 47 | // Register the summary and the histogram with Prometheus's default registry. 48 | prometheus.MustRegister( 49 | uptime, 50 | RequestDuration, 51 | RedisOperationLatency, 52 | ) 53 | 54 | go recordUptime() 55 | } 56 | 57 | // recordUptime increases service uptime per second. 58 | func recordUptime() { 59 | for range time.Tick(time.Second) { 60 | uptime.Inc() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /center/router/router_chart_share.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ccfos/nightingale/v6/models" 7 | "github.com/ccfos/nightingale/v6/pkg/strx" 8 | 9 | "github.com/gin-gonic/gin" 10 | "github.com/toolkits/pkg/ginx" 11 | ) 12 | 13 | func (rt *Router) chartShareGets(c *gin.Context) { 14 | ids := ginx.QueryStr(c, "ids", "") 15 | lst, err := models.ChartShareGetsByIds(rt.Ctx, strx.IdsInt64ForAPI(ids, ",")) 16 | ginx.NewRender(c).Data(lst, err) 17 | } 18 | 19 | type chartShareForm struct { 20 | DatasourceId int64 `json:"datasource_id"` 21 | Configs string `json:"configs"` 22 | } 23 | 24 | func (rt *Router) chartShareAdd(c *gin.Context) { 25 | username := c.MustGet("username").(string) 26 | 27 | var forms []chartShareForm 28 | ginx.BindJSON(c, &forms) 29 | 30 | ids := []int64{} 31 | now := time.Now().Unix() 32 | 33 | for _, f := range forms { 34 | chart := models.ChartShare{ 35 | DatasourceId: f.DatasourceId, 36 | Configs: f.Configs, 37 | CreateBy: username, 38 | CreateAt: now, 39 | } 40 | ginx.Dangerous(chart.Add(rt.Ctx)) 41 | ids = append(ids, chart.Id) 42 | } 43 | 44 | ginx.NewRender(c).Data(ids, nil) 45 | } 46 | -------------------------------------------------------------------------------- /center/router/router_crypto.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/pkg/secu" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/toolkits/pkg/ginx" 8 | ) 9 | 10 | type confPropCrypto struct { 11 | Data string `json:"data" binding:"required"` 12 | Key string `json:"key" binding:"required"` 13 | } 14 | 15 | func (rt *Router) confPropEncrypt(c *gin.Context) { 16 | var f confPropCrypto 17 | ginx.BindJSON(c, &f) 18 | 19 | k := len(f.Key) 20 | switch k { 21 | default: 22 | c.String(400, "The key length should be 16, 24 or 32") 23 | return 24 | case 16, 24, 32: 25 | break 26 | } 27 | 28 | s, err := secu.DealWithEncrypt(f.Data, f.Key) 29 | if err != nil { 30 | c.String(500, err.Error()) 31 | } 32 | 33 | c.JSON(200, gin.H{ 34 | "src": f.Data, 35 | "key": f.Key, 36 | "encrypt": s, 37 | }) 38 | } 39 | 40 | func (rt *Router) confPropDecrypt(c *gin.Context) { 41 | var f confPropCrypto 42 | ginx.BindJSON(c, &f) 43 | 44 | k := len(f.Key) 45 | switch k { 46 | default: 47 | c.String(400, "The key length should be 16, 24 or 32") 48 | return 49 | case 16, 24, 32: 50 | break 51 | } 52 | 53 | s, err := secu.DealWithDecrypt(f.Data, f.Key) 54 | if err != nil { 55 | c.String(500, err.Error()) 56 | } 57 | 58 | c.JSON(200, gin.H{ 59 | "src": f.Data, 60 | "key": f.Key, 61 | "decrypt": s, 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /center/router/router_dashboard.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | type ChartPure struct { 4 | Configs string `json:"configs"` 5 | Weight int `json:"weight"` 6 | } 7 | 8 | type ChartGroupPure struct { 9 | Name string `json:"name"` 10 | Weight int `json:"weight"` 11 | Charts []ChartPure `json:"charts"` 12 | } 13 | 14 | type DashboardPure struct { 15 | Name string `json:"name"` 16 | Tags string `json:"tags"` 17 | Configs string `json:"configs"` 18 | ChartGroups []ChartGroupPure `json:"chart_groups"` 19 | } 20 | -------------------------------------------------------------------------------- /center/router/router_notify_channel_test.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGetFlashDutyChannels(t *testing.T) { 9 | // 构造测试数据 10 | integrationUrl := "https://api.flashcat.cloud/event/push/alert/n9e?integration_key=xxx" 11 | jsonData := []byte(`{}`) 12 | 13 | // 调用被测试的函数 14 | channels, err := getFlashDutyChannels(integrationUrl, jsonData) 15 | 16 | fmt.Println(channels, err) 17 | } 18 | -------------------------------------------------------------------------------- /center/router/router_server.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ccfos/nightingale/v6/models" 7 | 8 | "github.com/gin-gonic/gin" 9 | "github.com/toolkits/pkg/ginx" 10 | ) 11 | 12 | func (rt *Router) serversGet(c *gin.Context) { 13 | list, err := models.AlertingEngineGets(rt.Ctx, "") 14 | ginx.NewRender(c).Data(list, err) 15 | } 16 | 17 | func (rt *Router) serverClustersGet(c *gin.Context) { 18 | list, err := models.AlertingEngineGetsClusters(rt.Ctx, "") 19 | ginx.NewRender(c).Data(list, err) 20 | } 21 | 22 | func (rt *Router) serverHeartbeat(c *gin.Context) { 23 | var req models.HeartbeatInfo 24 | ginx.BindJSON(c, &req) 25 | err := models.AlertingEngineHeartbeatWithCluster(rt.Ctx, req.Instance, req.EngineCluster, req.DatasourceId) 26 | ginx.NewRender(c).Message(err) 27 | } 28 | 29 | func (rt *Router) serversActive(c *gin.Context) { 30 | datasourceId := ginx.QueryInt64(c, "dsid", 0) 31 | engineName := ginx.QueryStr(c, "engine_name", "") 32 | if engineName != "" { 33 | servers, err := models.AlertingEngineGetsInstances(rt.Ctx, "engine_cluster = ? and clock > ?", engineName, time.Now().Unix()-30) 34 | ginx.NewRender(c).Data(servers, err) 35 | return 36 | } 37 | 38 | if datasourceId == 0 { 39 | ginx.NewRender(c).Message("dsid is required") 40 | return 41 | } 42 | servers, err := models.AlertingEngineGetsInstances(rt.Ctx, "datasource_id = ? and clock > ?", datasourceId, time.Now().Unix()-30) 43 | ginx.NewRender(c).Data(servers, err) 44 | } 45 | -------------------------------------------------------------------------------- /center/router/router_source_token.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/ccfos/nightingale/v6/models" 8 | "github.com/google/uuid" 9 | 10 | "github.com/gin-gonic/gin" 11 | "github.com/toolkits/pkg/ginx" 12 | ) 13 | 14 | // sourceTokenAdd 生成新的源令牌 15 | func (rt *Router) sourceTokenAdd(c *gin.Context) { 16 | var f models.SourceToken 17 | ginx.BindJSON(c, &f) 18 | 19 | if f.ExpireAt > 0 && f.ExpireAt <= time.Now().Unix() { 20 | ginx.Bomb(http.StatusBadRequest, "expire time must be in the future") 21 | } 22 | 23 | token := uuid.New().String() 24 | 25 | username := c.MustGet("username").(string) 26 | 27 | f.Token = token 28 | f.CreateBy = username 29 | f.CreateAt = time.Now().Unix() 30 | 31 | err := f.Add(rt.Ctx) 32 | ginx.Dangerous(err) 33 | 34 | go models.CleanupExpiredTokens(rt.Ctx) 35 | ginx.NewRender(c).Data(token, nil) 36 | } 37 | -------------------------------------------------------------------------------- /center/sso/sync.go: -------------------------------------------------------------------------------- 1 | package sso 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ccfos/nightingale/v6/pkg/ctx" 7 | "github.com/toolkits/pkg/logger" 8 | ) 9 | 10 | func (s *SsoClient) SyncSsoUsers(ctx *ctx.Context) { 11 | if err := s.LDAP.SyncAddAndDelUsers(ctx); err != nil { 12 | fmt.Println("failed to sync the addition and deletion of ldap users:", err) 13 | } 14 | 15 | if err := s.LDAP.SyncDelUsers(ctx); err != nil { 16 | fmt.Println("failed to sync deletion of ldap users:", err) 17 | } 18 | 19 | go s.loopSyncSsoUsers(ctx) 20 | } 21 | 22 | func (s *SsoClient) loopSyncSsoUsers(ctx *ctx.Context) { 23 | for { 24 | select { 25 | case <-s.LDAP.Ticker.C: 26 | lc := s.LDAP.Copy() 27 | 28 | if err := lc.SyncAddAndDelUsers(ctx); err != nil { 29 | logger.Warningf("failed to sync the addition and deletion of ldap users: %v", err) 30 | } 31 | 32 | if err := lc.SyncDelUsers(ctx); err != nil { 33 | logger.Warningf("failed to sync deletion of ldap users: %v", err) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/cli/upgrade" 5 | ) 6 | 7 | func Upgrade(configFile string) error { 8 | return upgrade.Upgrade(configFile) 9 | } 10 | -------------------------------------------------------------------------------- /cli/upgrade/config.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "bytes" 5 | "path" 6 | 7 | "github.com/ccfos/nightingale/v6/pkg/cfg" 8 | "github.com/ccfos/nightingale/v6/pkg/ormx" 9 | "github.com/ccfos/nightingale/v6/pkg/tlsx" 10 | "github.com/koding/multiconfig" 11 | ) 12 | 13 | type Config struct { 14 | DB ormx.DBConfig 15 | Clusters []ClusterOptions 16 | } 17 | 18 | type ClusterOptions struct { 19 | Name string 20 | Prom string 21 | 22 | BasicAuthUser string 23 | BasicAuthPass string 24 | 25 | Headers []string 26 | 27 | Timeout int64 28 | DialTimeout int64 29 | 30 | UseTLS bool 31 | tlsx.ClientConfig 32 | 33 | MaxIdleConnsPerHost int 34 | } 35 | 36 | func Parse(fpath string, configPtr *Config) error { 37 | var ( 38 | tBuf []byte 39 | ) 40 | loaders := []multiconfig.Loader{ 41 | &multiconfig.TagLoader{}, 42 | &multiconfig.EnvironmentLoader{}, 43 | } 44 | s := cfg.NewFileScanner() 45 | 46 | s.Read(path.Join(fpath)) 47 | tBuf = append(tBuf, s.Data()...) 48 | tBuf = append(tBuf, []byte("\n")...) 49 | 50 | if s.Err() != nil { 51 | return s.Err() 52 | } 53 | 54 | if len(tBuf) != 0 { 55 | loaders = append(loaders, &multiconfig.TOMLLoader{Reader: bytes.NewReader(tBuf)}) 56 | } 57 | 58 | m := multiconfig.DefaultLoader{ 59 | Loader: multiconfig.MultiLoader(loaders...), 60 | Validator: multiconfig.MultiValidator(&multiconfig.RequiredValidator{}), 61 | } 62 | return m.Load(configPtr) 63 | } 64 | -------------------------------------------------------------------------------- /cli/upgrade/readme.md: -------------------------------------------------------------------------------- 1 | # v5 升级 v6 手册 2 | 0. 操作之前,记得备注下数据库! 3 | 4 | 1. 需要先将你正在使用的夜莺数据源表结构更新到和 v5.15.0 一致,[release](https://github.com/ccfos/nightingale/releases) 页面有每个版本表结构的更新说明,可以根据你正在使用的版本,按照说明,逐个执行的更新表结构的语句 5 | 6 | 2. 解压 n9e 安装包,导入 upgrade.sql 到 n9e_v5 数据库 7 | ``` 8 | mysql -h 127.0.0.1 -u root -p1234 < cli/upgrade/upgrade.sql 9 | ``` 10 | 11 | 3. 执行 n9e-cli 完成数据库表结构升级, webapi.conf 为 v5 版本 n9e-webapi 正在使用的配置文件 12 | ``` 13 | ./n9e-cli --upgrade --config webapi.conf 14 | ``` 15 | 16 | 4. 修改 n9e 配置文件中的数据库为 n9e_v5,启动 n9e 进程 17 | ``` 18 | nohup ./n9e &> n9e.log & 19 | ``` 20 | 21 | 5. n9e 监听的端口为 17000,需要将之前的 web 端口和数据上报的端口,都调整为 17000 -------------------------------------------------------------------------------- /cmd/cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/ccfos/nightingale/v6/cli" 9 | "github.com/ccfos/nightingale/v6/pkg/version" 10 | ) 11 | 12 | var ( 13 | upgrade = flag.Bool("upgrade", false, "Upgrade the database.") 14 | showVersion = flag.Bool("version", false, "Show version.") 15 | configFile = flag.String("config", "", "Specify webapi.conf of v5.x version") 16 | ) 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | if *showVersion { 22 | fmt.Println(version.Version) 23 | os.Exit(0) 24 | } 25 | 26 | if *upgrade { 27 | if *configFile == "" { 28 | fmt.Println("Please specify the configuration directory.") 29 | os.Exit(1) 30 | } 31 | 32 | err := cli.Upgrade(*configFile) 33 | if err != nil { 34 | fmt.Println(err) 35 | os.Exit(1) 36 | } 37 | fmt.Print("Upgrade successfully.") 38 | os.Exit(0) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cmd/edge/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | 11 | "github.com/ccfos/nightingale/v6/pkg/osx" 12 | "github.com/ccfos/nightingale/v6/pkg/version" 13 | 14 | "github.com/toolkits/pkg/runner" 15 | ) 16 | 17 | var ( 18 | showVersion = flag.Bool("version", false, "Show version.") 19 | configDir = flag.String("configs", osx.GetEnv("N9E_EDGE_CONFIGS", "etc"), "Specify configuration directory.(env:N9E_EDGE_CONFIGS)") 20 | cryptoKey = flag.String("crypto-key", "", "Specify the secret key for configuration file field encryption.") 21 | ) 22 | 23 | func main() { 24 | flag.Parse() 25 | 26 | if *showVersion { 27 | fmt.Println(version.Version) 28 | os.Exit(0) 29 | } 30 | 31 | printEnv() 32 | 33 | cleanFunc, err := Initialize(*configDir, *cryptoKey) 34 | if err != nil { 35 | log.Fatalln("failed to initialize:", err) 36 | } 37 | 38 | code := 1 39 | sc := make(chan os.Signal, 1) 40 | signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 41 | 42 | EXIT: 43 | for { 44 | sig := <-sc 45 | fmt.Println("received signal:", sig.String()) 46 | switch sig { 47 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: 48 | code = 0 49 | break EXIT 50 | case syscall.SIGHUP: 51 | // reload configuration? 52 | default: 53 | break EXIT 54 | } 55 | } 56 | 57 | cleanFunc() 58 | fmt.Println("process exited") 59 | os.Exit(code) 60 | } 61 | 62 | func printEnv() { 63 | runner.Init() 64 | fmt.Println("runner.cwd:", runner.Cwd) 65 | fmt.Println("runner.hostname:", runner.Hostname) 66 | fmt.Println("runner.fd_limits:", runner.FdLimits()) 67 | fmt.Println("runner.vm_limits:", runner.VMLimits()) 68 | } 69 | -------------------------------------------------------------------------------- /conf/crypto.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ccfos/nightingale/v6/pkg/secu" 7 | ) 8 | 9 | func decryptConfig(config *ConfigType, cryptoKey string) error { 10 | decryptDsn, err := secu.DealWithDecrypt(config.DB.DSN, cryptoKey) 11 | if err != nil { 12 | return fmt.Errorf("failed to decrypt the db dsn: %s", err) 13 | } 14 | 15 | config.DB.DSN = decryptDsn 16 | 17 | for k := range config.HTTP.APIForService.BasicAuth { 18 | decryptPwd, err := secu.DealWithDecrypt(config.HTTP.APIForService.BasicAuth[k], cryptoKey) 19 | if err != nil { 20 | return fmt.Errorf("failed to decrypt http basic auth password: %s", err) 21 | } 22 | 23 | config.HTTP.APIForService.BasicAuth[k] = decryptPwd 24 | } 25 | 26 | for k := range config.HTTP.APIForAgent.BasicAuth { 27 | decryptPwd, err := secu.DealWithDecrypt(config.HTTP.APIForAgent.BasicAuth[k], cryptoKey) 28 | if err != nil { 29 | return fmt.Errorf("failed to decrypt http basic auth password: %s", err) 30 | } 31 | 32 | config.HTTP.APIForAgent.BasicAuth[k] = decryptPwd 33 | } 34 | 35 | for i, v := range config.Pushgw.Writers { 36 | decryptWriterPwd, err := secu.DealWithDecrypt(v.BasicAuthPass, cryptoKey) 37 | if err != nil { 38 | return fmt.Errorf("failed to decrypt writer basic auth password: %s", err) 39 | } 40 | 41 | config.Pushgw.Writers[i].BasicAuthPass = decryptWriterPwd 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /cron/clean_notify_record.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ccfos/nightingale/v6/models" 7 | "github.com/ccfos/nightingale/v6/pkg/ctx" 8 | 9 | "github.com/robfig/cron/v3" 10 | "github.com/toolkits/pkg/logger" 11 | ) 12 | 13 | func cleanNotifyRecord(ctx *ctx.Context, day int) { 14 | lastWeek := time.Now().Unix() - 86400*int64(day) 15 | err := models.DB(ctx).Model(&models.NotificaitonRecord{}).Where("created_at < ?", lastWeek).Delete(&models.NotificaitonRecord{}).Error 16 | if err != nil { 17 | logger.Errorf("Failed to clean notify record: %v", err) 18 | } 19 | 20 | } 21 | 22 | // 每天凌晨1点执行清理任务 23 | func CleanNotifyRecord(ctx *ctx.Context, day int) { 24 | c := cron.New() 25 | if day < 1 { 26 | day = 7 27 | } 28 | 29 | // 使用cron表达式设置每天凌晨1点执行 30 | _, err := c.AddFunc("0 1 * * *", func() { 31 | cleanNotifyRecord(ctx, day) 32 | }) 33 | 34 | if err != nil { 35 | logger.Errorf("Failed to add clean notify record cron job: %v", err) 36 | return 37 | } 38 | 39 | // 启动cron任务 40 | c.Start() 41 | } 42 | -------------------------------------------------------------------------------- /datasource/prom/prom.go: -------------------------------------------------------------------------------- 1 | package prom 2 | 3 | type Prometheus struct { 4 | PrometheusAddr string `json:"prometheus.addr"` 5 | PrometheusBasic struct { 6 | PrometheusUser string `json:"prometheus.user"` 7 | PrometheusPass string `json:"prometheus.password"` 8 | } `json:"prometheus.basic"` 9 | Headers map[string]string `json:"prometheus.headers"` 10 | PrometheusTimeout int64 `json:"prometheus.timeout"` 11 | ClusterName string `json:"prometheus.cluster_name"` 12 | WriteAddr string `json:"prometheus.write_addr"` 13 | TsdbType string `json:"prometheus.tsdb_type"` 14 | InternalAddr string `json:"prometheus.internal_addr"` 15 | } 16 | -------------------------------------------------------------------------------- /doc/active-contributors.md: -------------------------------------------------------------------------------- 1 | ## Active Contributors 2 | 3 | - [xiaoziv](https://github.com/xiaoziv) 4 | - [tanxiao1990](https://github.com/tanxiao1990) 5 | - [bbaobelief](https://github.com/bbaobelief) 6 | - [freedomkk-qfeng](https://github.com/freedomkk-qfeng) 7 | - [lsy1990](https://github.com/lsy1990) 8 | -------------------------------------------------------------------------------- /doc/committers.md: -------------------------------------------------------------------------------- 1 | ## Committers 2 | 3 | - [YeningQin](https://github.com/710leo) 4 | - [FeiKong](https://github.com/kongfei605) 5 | - [XiaqingDai](https://github.com/jsers) 6 | -------------------------------------------------------------------------------- /doc/contributors.md: -------------------------------------------------------------------------------- 1 | ## Contributors 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /doc/end-users.md: -------------------------------------------------------------------------------- 1 | ## End Users 2 | 3 | - [中移动](https://github.com/ccfos/nightingale/issues/897#issuecomment-1086573166) 4 | - [inke](https://github.com/ccfos/nightingale/issues/897#issuecomment-1099840636) 5 | - [方正证券](https://github.com/ccfos/nightingale/issues/897#issuecomment-1110492461) 6 | -------------------------------------------------------------------------------- /doc/img/Nightingale_L_V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/Nightingale_L_V.png -------------------------------------------------------------------------------- /doc/img/alert-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/alert-events.png -------------------------------------------------------------------------------- /doc/img/arch-product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/arch-product.png -------------------------------------------------------------------------------- /doc/img/arch-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/arch-system.png -------------------------------------------------------------------------------- /doc/img/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/arch.png -------------------------------------------------------------------------------- /doc/img/ccf-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/ccf-logo.png -------------------------------------------------------------------------------- /doc/img/ccf-n9e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/ccf-n9e.png -------------------------------------------------------------------------------- /doc/img/dingtalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/dingtalk.png -------------------------------------------------------------------------------- /doc/img/install-vm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/install-vm.png -------------------------------------------------------------------------------- /doc/img/intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/intro.gif -------------------------------------------------------------------------------- /doc/img/mysql-alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/mysql-alerts.png -------------------------------------------------------------------------------- /doc/img/n9e-arch-latest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/n9e-arch-latest.png -------------------------------------------------------------------------------- /doc/img/n9e-node-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/n9e-node-dashboard.png -------------------------------------------------------------------------------- /doc/img/n9e-screenshot-gif-v6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/n9e-screenshot-gif-v6.gif -------------------------------------------------------------------------------- /doc/img/n9e-vx-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/n9e-vx-new.png -------------------------------------------------------------------------------- /doc/img/nightingale_logo_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/nightingale_logo_h.png -------------------------------------------------------------------------------- /doc/img/nightingale_logo_v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/nightingale_logo_v.png -------------------------------------------------------------------------------- /doc/img/readme/20240221152601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/20240221152601.png -------------------------------------------------------------------------------- /doc/img/readme/20240222102119.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/20240222102119.png -------------------------------------------------------------------------------- /doc/img/readme/20240513103305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/20240513103305.png -------------------------------------------------------------------------------- /doc/img/readme/20240513103530.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/20240513103530.png -------------------------------------------------------------------------------- /doc/img/readme/20240513103628.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/20240513103628.png -------------------------------------------------------------------------------- /doc/img/readme/20240513103825.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/20240513103825.png -------------------------------------------------------------------------------- /doc/img/readme/2025-05-23_18-43-37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/2025-05-23_18-43-37.png -------------------------------------------------------------------------------- /doc/img/readme/2025-05-23_18-46-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/2025-05-23_18-46-06.png -------------------------------------------------------------------------------- /doc/img/readme/2025-05-23_18-49-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/2025-05-23_18-49-02.png -------------------------------------------------------------------------------- /doc/img/readme/2025-05-30_08-49-28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/2025-05-30_08-49-28.png -------------------------------------------------------------------------------- /doc/img/readme/logos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/logos.png -------------------------------------------------------------------------------- /doc/img/readme/n9e-switch-i18n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/readme/n9e-switch-i18n.png -------------------------------------------------------------------------------- /doc/img/redis-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/redis-dash.png -------------------------------------------------------------------------------- /doc/img/vm-cluster-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/vm-cluster-arch.png -------------------------------------------------------------------------------- /doc/img/wecom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/wecom.png -------------------------------------------------------------------------------- /doc/img/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/doc/img/wx.jpg -------------------------------------------------------------------------------- /doc/pmc.md: -------------------------------------------------------------------------------- 1 | ### PMC Chair 2 | - [laiwei](https://github.com/laiwei) 3 | 4 | ### PMC Co-Chair 5 | - [UlricQin](https://github.com/UlricQin) 6 | 7 | ### PMC Member 8 | -------------------------------------------------------------------------------- /docker/.dockerignore: -------------------------------------------------------------------------------- 1 | compose-host-network 2 | compose-postgres 3 | compose-bridge 4 | initsql 5 | build.sh 6 | -------------------------------------------------------------------------------- /docker/Dockerfile.goreleaser: -------------------------------------------------------------------------------- 1 | FROM --platform=$TARGETPLATFORM python:3-slim 2 | 3 | 4 | WORKDIR /app 5 | ADD n9e /app/ 6 | ADD etc /app/etc/ 7 | ADD integrations /app/integrations/ 8 | RUN pip install requests 9 | 10 | EXPOSE 17000 11 | 12 | CMD ["/app/n9e", "-h"] 13 | -------------------------------------------------------------------------------- /docker/Dockerfile.goreleaser.arm64: -------------------------------------------------------------------------------- 1 | FROM --platform=$TARGETPLATFORM python:3-slim 2 | 3 | 4 | WORKDIR /app 5 | ADD n9e /app/ 6 | ADD etc /app/etc/ 7 | ADD integrations /app/integrations/ 8 | 9 | EXPOSE 17000 10 | 11 | CMD ["/app/n9e", "-h"] 12 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ $# -ne 1 ]; then 3 | echo "$0 " 4 | exit 0 5 | fi 6 | 7 | tag=$1 8 | 9 | echo "tag: ${tag}" 10 | 11 | rm -rf n9e pub 12 | cp ../n9e . 13 | 14 | docker build -t nightingale:${tag} . 15 | 16 | docker tag nightingale:${tag} ulric2019/nightingale:${tag} 17 | docker push ulric2019/nightingale:${tag} 18 | 19 | rm -rf n9e pub 20 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.cpu/cpu.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect per cpu 5 | # collect_per_cpu = false 6 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.disk/disk.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default stats will be gathered for all mount points. 5 | # # Set mount_points will restrict the stats to only the specified mount points. 6 | mount_points = ["/"] 7 | 8 | # Ignore mount points by filesystem type. 9 | ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs", "nsfs"] 10 | 11 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.diskio/diskio.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default, categraf will gather stats for all devices including disk partitions. 5 | # # Setting devices will restrict the stats to the specified devices. 6 | # devices = ["sda", "sdb", "vd*"] -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.kernel/kernel.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.mem/mem.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect platform specified metrics 5 | collect_platform_fields = true 6 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.mysql/mysql.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | address = "mysql:3306" 3 | username = "root" 4 | password = "1234" 5 | 6 | # # set tls=custom to enable tls 7 | # parameters = "tls=false" 8 | 9 | # extra_status_metrics = true 10 | # extra_innodb_metrics = false 11 | # gather_processlist_processes_by_state = false 12 | # gather_processlist_processes_by_user = false 13 | # gather_schema_size = true 14 | # gather_table_size = false 15 | # gather_system_table_size = false 16 | # gather_slave_status = true 17 | 18 | # # timeout 19 | # timeout_seconds = 3 20 | 21 | # # interval = global.interval * interval_times 22 | # interval_times = 1 23 | 24 | # important! use global unique string to specify instance 25 | labels = { instance="docker-compose-mysql" } 26 | 27 | ## Optional TLS Config 28 | # use_tls = false 29 | # tls_min_version = "1.2" 30 | # tls_ca = "/etc/categraf/ca.pem" 31 | # tls_cert = "/etc/categraf/cert.pem" 32 | # tls_key = "/etc/categraf/key.pem" 33 | ## Use TLS but skip chain & host verification 34 | # insecure_skip_verify = true 35 | 36 | #[[instances.queries]] 37 | # mesurement = "lock_wait" 38 | # metric_fields = [ "total" ] 39 | # timeout = "3s" 40 | # request = ''' 41 | #SELECT count(*) as total FROM information_schema.innodb_trx WHERE trx_state='LOCK WAIT' 42 | #''' 43 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.net/net.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect protocol stats on Linux 5 | # collect_protocol_stats = false 6 | 7 | # # setting interfaces will tell categraf to gather these explicit interfaces 8 | # interfaces = ["eth0"] -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.netstat/netstat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.processes/processes.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # force use ps command to gather 5 | # force_ps = false 6 | 7 | # # force use /proc to gather 8 | # force_proc = false -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.prometheus/prometheus.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | urls = [ 3 | "http://nightingale:17000/metrics" 4 | ] -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.redis/redis.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | address = "redis:6379" 3 | username = "" 4 | password = "" 5 | # pool_size = 2 6 | 7 | ## 是否开启slowlog 收集 8 | # gather_slowlog = true 9 | ## 最多收集少条slowlog 10 | # slowlog_max_len = 100 11 | ## 收集距离现在多少秒以内的slowlog 12 | ## 注意插件的采集周期,该参数不要小于采集周期,否则会有slowlog查不到 13 | # slowlog_time_window=30 14 | 15 | # 指标 16 | # redis_slow_log{ident=dev-01 client_addr=127.0.0.1:56364 client_name= cmd="info ALL" log_id=983} 74 (单位微秒) 17 | 18 | # # Optional. Specify redis commands to retrieve values 19 | # commands = [ 20 | # {command = ["get", "sample-key1"], metric = "custom_metric_name1"}, 21 | # {command = ["get", "sample-key2"], metric = "custom_metric_name2"} 22 | # ] 23 | 24 | # # interval = global.interval * interval_times 25 | # interval_times = 1 26 | 27 | # important! use global unique string to specify instance 28 | labels = { instance="docker-compose-redis" } 29 | 30 | ## Optional TLS Config 31 | # use_tls = false 32 | # tls_min_version = "1.2" 33 | # tls_ca = "/etc/categraf/ca.pem" 34 | # tls_cert = "/etc/categraf/cert.pem" 35 | # tls_key = "/etc/categraf/key.pem" 36 | ## Use TLS but skip chain & host verification 37 | # insecure_skip_verify = true 38 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-categraf/input.system/system.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect metric: system_n_users 5 | # collect_user_number = false 6 | -------------------------------------------------------------------------------- /docker/compose-bridge/etc-mysql/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | pid-file = /var/run/mysqld/mysqld.pid 3 | socket = /var/run/mysqld/mysqld.sock 4 | datadir = /var/lib/mysql 5 | bind-address = 0.0.0.0 -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.cpu/cpu.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect per cpu 5 | # collect_per_cpu = false 6 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.disk/disk.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default stats will be gathered for all mount points. 5 | # # Set mount_points will restrict the stats to only the specified mount points. 6 | # mount_points = ["/"] 7 | 8 | # Ignore mount points by filesystem type. 9 | ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] 10 | 11 | ignore_mount_points = ["/boot"] 12 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.diskio/diskio.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default, categraf will gather stats for all devices including disk partitions. 5 | # # Setting devices will restrict the stats to the specified devices. 6 | # devices = ["sda", "sdb", "vd*"] -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.kernel/kernel.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.mem/mem.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect platform specified metrics 5 | collect_platform_fields = true 6 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.net/net.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect protocol stats on Linux 5 | # collect_protocol_stats = false 6 | 7 | # # setting interfaces will tell categraf to gather these explicit interfaces 8 | # interfaces = ["eth0"] -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.netstat/netstat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.processes/processes.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # force use ps command to gather 5 | # force_ps = false 6 | 7 | # # force use /proc to gather 8 | # force_proc = false -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-categraf/input.system/system.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect metric: system_n_users 5 | # collect_user_number = false 6 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-logstash/logstash.yaml: -------------------------------------------------------------------------------- 1 | input { 2 | kafka { 3 | bootstrap_servers => "127.0.0.1:9092" 4 | topics => ["flashcatcloud"] 5 | codec => json 6 | type => n9e 7 | } 8 | } 9 | 10 | filter { 11 | grok { 12 | match => {"message" => "%{LOGLEVEL:status}"} 13 | overwrite => ["status"] 14 | } 15 | } 16 | 17 | output { 18 | elasticsearch { 19 | hosts => ["127.0.0.1:9200"] 20 | index => "n9e-%{+YYYY.MM.DD}" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-mysql/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | pid-file = /var/run/mysqld/mysqld.pid 3 | socket = /var/run/mysqld/mysqld.sock 4 | datadir = /var/lib/mysql 5 | bind-address = 127.0.0.1 -------------------------------------------------------------------------------- /docker/compose-host-network-metric-log/etc-prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 4 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Alertmanager configuration 8 | alerting: 9 | alertmanagers: 10 | - static_configs: 11 | - targets: 12 | # - alertmanager:9093 13 | 14 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 15 | rule_files: 16 | # - "first_rules.yml" 17 | # - "second_rules.yml" 18 | 19 | scrape_configs: 20 | # The job name is added as a label `job=` to any timeseries scraped from this config. 21 | - job_name: 'prometheus' 22 | static_configs: 23 | - targets: ['localhost:9090'] 24 | 25 | - job_name: 'nightingale' 26 | static_configs: 27 | - targets: ['localhost:17000'] 28 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.cpu/cpu.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect per cpu 5 | # collect_per_cpu = false 6 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.disk/disk.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default stats will be gathered for all mount points. 5 | # # Set mount_points will restrict the stats to only the specified mount points. 6 | # mount_points = ["/"] 7 | 8 | # Ignore mount points by filesystem type. 9 | ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] 10 | 11 | ignore_mount_points = ["/boot"] 12 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.diskio/diskio.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default, categraf will gather stats for all devices including disk partitions. 5 | # # Setting devices will restrict the stats to the specified devices. 6 | # devices = ["sda", "sdb", "vd*"] -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.kernel/kernel.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.mem/mem.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect platform specified metrics 5 | collect_platform_fields = true 6 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.net/net.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect protocol stats on Linux 5 | # collect_protocol_stats = false 6 | 7 | # # setting interfaces will tell categraf to gather these explicit interfaces 8 | # interfaces = ["eth0"] -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.netstat/netstat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.processes/processes.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # force use ps command to gather 5 | # force_ps = false 6 | 7 | # # force use /proc to gather 8 | # force_proc = false -------------------------------------------------------------------------------- /docker/compose-host-network/etc-categraf/input.system/system.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect metric: system_n_users 5 | # collect_user_number = false 6 | -------------------------------------------------------------------------------- /docker/compose-host-network/etc-mysql/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | pid-file = /var/run/mysqld/mysqld.pid 3 | socket = /var/run/mysqld/mysqld.sock 4 | datadir = /var/lib/mysql 5 | bind-address = 127.0.0.1 -------------------------------------------------------------------------------- /docker/compose-host-network/etc-prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 4 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Alertmanager configuration 8 | alerting: 9 | alertmanagers: 10 | - static_configs: 11 | - targets: 12 | # - alertmanager:9093 13 | 14 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 15 | rule_files: 16 | # - "first_rules.yml" 17 | # - "second_rules.yml" 18 | 19 | scrape_configs: 20 | # The job name is added as a label `job=` to any timeseries scraped from this config. 21 | - job_name: 'prometheus' 22 | static_configs: 23 | - targets: ['localhost:9090'] 24 | 25 | - job_name: 'nightingale' 26 | static_configs: 27 | - targets: ['localhost:17000'] 28 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.cpu/cpu.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect per cpu 5 | # collect_per_cpu = false 6 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.disk/disk.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default stats will be gathered for all mount points. 5 | # # Set mount_points will restrict the stats to only the specified mount points. 6 | # mount_points = ["/"] 7 | 8 | # Ignore mount points by filesystem type. 9 | ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] 10 | 11 | ignore_mount_points = ["/boot"] 12 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.diskio/diskio.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # By default, categraf will gather stats for all devices including disk partitions. 5 | # # Setting devices will restrict the stats to the specified devices. 6 | # devices = ["sda", "sdb", "vd*"] -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.kernel/kernel.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.mem/mem.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect platform specified metrics 5 | collect_platform_fields = true 6 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.net/net.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect protocol stats on Linux 5 | # collect_protocol_stats = false 6 | 7 | # # setting interfaces will tell categraf to gather these explicit interfaces 8 | # interfaces = ["eth0"] -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.netstat/netstat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.processes/processes.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # force use ps command to gather 5 | # force_ps = false 6 | 7 | # # force use /proc to gather 8 | # force_proc = false -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/input.system/system.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # whether collect metric: system_n_users 5 | # collect_user_number = false 6 | -------------------------------------------------------------------------------- /docker/compose-postgres/categraf/conf/prometheus.toml: -------------------------------------------------------------------------------- 1 | [prometheus] 2 | enable=true 3 | scrape_config_file="/etc/prometheus/prometheus.yml" 4 | ## log level, debug warn info error 5 | log_level="info" 6 | ## wal file storage path ,default ./data-agent 7 | # wal_storage_path="/path/to/storage" 8 | ## wal reserve time duration, default value is 2 hour 9 | # wal_min_duration=2 10 | 11 | -------------------------------------------------------------------------------- /docker/compose-postgres/prometc_vm/prometheus.yml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 4 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # A scrape configuration containing exactly one endpoint to scrape: 8 | # Here it's Prometheus itself. 9 | scrape_configs: 10 | # The job name is added as a label `job=` to any timeseries scraped from this config. 11 | - job_name: 'victoriametrics' 12 | # metrics_path defaults to '/metrics' 13 | # scheme defaults to 'http'. 14 | static_configs: 15 | - targets: ['victoriametrics:8428'] 16 | 17 | - job_name: 'n9e' 18 | # static_configs: 19 | # - targets: ['n9e:17000'] 20 | file_sd_configs: 21 | - files: 22 | - targets.json 23 | 24 | remote_write: 25 | - url: 'http://n9e:17000/prometheus/v1/write' 26 | -------------------------------------------------------------------------------- /docker/compose-postgres/prometc_vm/targets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "targets": [ 4 | "n9e:17000" 5 | ] 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /docker/initsql/c-init.sql: -------------------------------------------------------------------------------- 1 | CREATE USER IF NOT EXISTS 'root'@'127.0.0.1' IDENTIFIED BY '1234'; 2 | GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION; 3 | 4 | CREATE USER IF NOT EXISTS 'root'@'localhost' IDENTIFIED BY '1234'; 5 | GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; 6 | 7 | CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '1234'; 8 | GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; 9 | 10 | FLUSH PRIVILEGES; 11 | -------------------------------------------------------------------------------- /dscache/cache.go: -------------------------------------------------------------------------------- 1 | package dscache 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/ccfos/nightingale/v6/datasource" 7 | "github.com/toolkits/pkg/logger" 8 | ) 9 | 10 | type Cache struct { 11 | datas map[string]map[int64]datasource.Datasource 12 | mutex *sync.RWMutex 13 | } 14 | 15 | var DsCache = Cache{ 16 | datas: make(map[string]map[int64]datasource.Datasource), 17 | mutex: new(sync.RWMutex), 18 | } 19 | 20 | func (cs *Cache) Put(cate string, dsId int64, ds datasource.Datasource) { 21 | cs.mutex.Lock() 22 | if _, found := cs.datas[cate]; !found { 23 | cs.datas[cate] = make(map[int64]datasource.Datasource) 24 | } 25 | 26 | if _, found := cs.datas[cate][dsId]; found { 27 | if cs.datas[cate][dsId].Equal(ds) { 28 | cs.mutex.Unlock() 29 | return 30 | } 31 | } 32 | cs.mutex.Unlock() 33 | 34 | // InitClient() 在用户配置错误或远端不可用时, 会非常耗时, mutex被长期持有, 导致Get()会超时 35 | err := ds.InitClient() 36 | if err != nil { 37 | logger.Errorf("init plugin:%s %d %+v client fail: %v", cate, dsId, ds, err) 38 | return 39 | } 40 | 41 | logger.Debugf("init plugin:%s %d %+v client success", cate, dsId, ds) 42 | cs.mutex.Lock() 43 | cs.datas[cate][dsId] = ds 44 | cs.mutex.Unlock() 45 | } 46 | 47 | func (cs *Cache) Get(cate string, dsId int64) (datasource.Datasource, bool) { 48 | cs.mutex.RLock() 49 | defer cs.mutex.RUnlock() 50 | if _, found := cs.datas[cate]; !found { 51 | return nil, false 52 | } 53 | 54 | if _, found := cs.datas[cate][dsId]; !found { 55 | return nil, false 56 | } 57 | 58 | return cs.datas[cate][dsId], true 59 | } 60 | -------------------------------------------------------------------------------- /dskit/clickhouse/clickhouse_test.go: -------------------------------------------------------------------------------- 1 | package clickhouse 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "testing" 8 | "time" 9 | 10 | "github.com/ccfos/nightingale/v6/dskit/types" 11 | ) 12 | 13 | func Test_Timeseries(t *testing.T) { 14 | ck := &Clickhouse{ 15 | Nodes: []string{"127.0.0.1:8123"}, 16 | User: "default", 17 | Password: "123456", 18 | } 19 | 20 | err := ck.InitCli() 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | data, err := ck.QueryTimeseries(context.TODO(), &QueryParam{ 26 | Sql: `select * from default.student limit 20`, 27 | From: time.Now().Unix() - 300, 28 | To: time.Now().Unix(), 29 | TimeField: "created_at", 30 | TimeFormat: "datetime", 31 | Keys: types.Keys{ 32 | LabelKey: "age", 33 | }, 34 | }) 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | bs, err := json.Marshal(data) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | fmt.Println(string(bs)) 43 | } 44 | -------------------------------------------------------------------------------- /dskit/clickhouse/timeseries.go: -------------------------------------------------------------------------------- 1 | package clickhouse 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/ccfos/nightingale/v6/dskit/sqlbase" 8 | "github.com/ccfos/nightingale/v6/dskit/types" 9 | ) 10 | 11 | const ( 12 | TimeFieldFormatEpochMilli = "epoch_millis" 13 | TimeFieldFormatEpochSecond = "epoch_second" 14 | ) 15 | 16 | // 时序数据相关的API 17 | type QueryParam struct { 18 | Limit int `json:"limit" mapstructure:"limit"` 19 | Sql string `json:"sql" mapstructure:"sql"` 20 | Ref string `json:"ref" mapstructure:"ref"` 21 | From int64 `json:"from" mapstructure:"from"` 22 | To int64 `json:"to" mapstructure:"to"` 23 | TimeField string `json:"time_field" mapstructure:"time_field"` 24 | TimeFormat string `json:"time_format" mapstructure:"time_format"` 25 | Keys types.Keys `json:"keys" mapstructure:"keys"` 26 | Database string `json:"database" mapstructure:"database"` 27 | Table string `json:"table" mapstructure:"table"` 28 | } 29 | 30 | var ( 31 | ckBannedOp = map[string]struct{}{ 32 | "CREATE": {}, 33 | "INSERT": {}, 34 | "ALTER": {}, 35 | "REVOKE": {}, 36 | "DROP": {}, 37 | "RENAME": {}, 38 | "ATTACH": {}, 39 | "DETACH": {}, 40 | "OPTIMIZE": {}, 41 | "TRUNCATE": {}, 42 | "SET": {}, 43 | } 44 | ) 45 | 46 | func (c *Clickhouse) QueryTimeseries(ctx context.Context, query *QueryParam) ([]types.MetricValues, error) { 47 | if query.Keys.ValueKey == "" { 48 | return nil, fmt.Errorf("valueKey is required") 49 | } 50 | 51 | rows, err := c.Query(ctx, query) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | // 构造成时续数据 57 | return sqlbase.FormatMetricValues(query.Keys, rows, true), nil 58 | } 59 | -------------------------------------------------------------------------------- /dskit/types/timeseries.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/prometheus/common/model" 9 | ) 10 | 11 | // 时序数据 12 | type MetricValues struct { 13 | Metric model.Metric `json:"metric"` 14 | Values [][]float64 `json:"values"` 15 | } 16 | 17 | type HistogramValues struct { 18 | Total int64 `json:"total"` 19 | Values [][]float64 `json:"values"` 20 | } 21 | 22 | // 瞬时值 23 | type AggregateValues struct { 24 | Labels map[string]string `json:"labels"` 25 | Values map[string]float64 `json:"values"` 26 | } 27 | 28 | // string 29 | func (m *MetricValues) String() string { 30 | var buf bytes.Buffer 31 | buf.WriteString(fmt.Sprintf("Metric: %+v ", m.Metric)) 32 | buf.WriteString("Values: ") 33 | for _, v := range m.Values { 34 | buf.WriteString(" [") 35 | for i, ts := range v { 36 | if i > 0 { 37 | buf.WriteString(", ") 38 | } 39 | buf.WriteString(strconv.FormatFloat(ts, 'f', -1, 64)) 40 | } 41 | buf.WriteString("] ") 42 | } 43 | return buf.String() 44 | } 45 | 46 | type Keys struct { 47 | ValueKey string `json:"valueKey" mapstructure:"valueKey"` // 多个用空格分隔 48 | LabelKey string `json:"labelKey" mapstructure:"labelKey"` // 多个用空格分隔 49 | TimeKey string `json:"timeKey" mapstructure:"timeKey"` 50 | TimeFormat string `json:"timeFormat" mapstructure:"timeFormat"` // not used anymore 51 | } 52 | -------------------------------------------------------------------------------- /dskit/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | LogExtractValueTypeLong = "long" 5 | LogExtractValueTypeFloat = "float" 6 | LogExtractValueTypeText = "text" 7 | LogExtractValueTypeDate = "date" 8 | LogExtractValueTypeBool = "bool" 9 | LogExtractValueTypeObject = "object" 10 | LogExtractValueTypeArray = "array" 11 | LogExtractValueTypeJSON = "json" 12 | ) 13 | 14 | type ColumnProperty struct { 15 | Field string `json:"field"` 16 | Type string `json:"type"` 17 | Type2 string `json:"type2,omitempty"` // field_property.Type 18 | Indexable bool `json:"indexable"` // 是否可以索引 19 | } 20 | -------------------------------------------------------------------------------- /dumper/dumper.go: -------------------------------------------------------------------------------- 1 | package dumper 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | // package level functions 6 | func ConfigRouter(r *gin.Engine) { 7 | syncDumper.ConfigRouter(r) 8 | } 9 | -------------------------------------------------------------------------------- /fe.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cp -f ./docker/initsql/a-n9e.sql n9e.sql 4 | 5 | if [ ! -d "./pub" ]; then 6 | TAG=$(curl -sX GET https://api.github.com/repos/n9e/fe/releases/latest | awk '/tag_name/{print $4;exit}' FS='[""]') 7 | 8 | if ! curl -o n9e-fe-${TAG}.tar.gz -L https://github.com/n9e/fe/releases/download/${TAG}/n9e-fe-${TAG}.tar.gz; then 9 | echo "failed to download n9e-fe-${TAG}.tar.gz!" 10 | exit 1 11 | fi 12 | 13 | if ! tar zxf n9e-fe-${TAG}.tar.gz; then 14 | echo "failed to untar n9e-fe-${TAG}.tar.gz!" 15 | exit 2 16 | fi 17 | fi 18 | 19 | GOPATH=$(go env GOPATH) 20 | GOPATH=${GOPATH:-/home/runner/go} 21 | 22 | # Embed files into a go binary 23 | # go install github.com/rakyll/statik 24 | if ! $GOPATH/bin/statik -src=./pub -dest=./front; then 25 | echo "failed to embed files into a go binary!" 26 | exit 4 27 | fi 28 | -------------------------------------------------------------------------------- /integrations/AMD_ROCm_SMI/collect/amd_rocm_smi/rocm.toml: -------------------------------------------------------------------------------- 1 | # Query statistics from AMD Graphics cards using rocm-smi binary 2 | # bin_path = "/opt/rocm/bin/rocm-smi" 3 | 4 | ## Optional: timeout for GPU polling 5 | # timeout = "5s" -------------------------------------------------------------------------------- /integrations/AMD_ROCm_SMI/icon/rocm_smi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AMD_ROCm_SMI/icon/rocm_smi.png -------------------------------------------------------------------------------- /integrations/AliYun/collect/aliyun/cloud.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 60 3 | [[instances]] 4 | # # endpoint region 参考 https://help.aliyun.com/document_detail/28616.html#section-72p-xhs-6qt 5 | # region="cn-beijing" 6 | # endpoint="metrics.cn-hangzhou.aliyuncs.com" 7 | # access_key_id="your-access-key-id" 8 | # access_key_secret="your-access-key-secret" 9 | # interval_times=4 10 | # delay="10m" 11 | # period="60s" 12 | # # namespace 参考 https://help.aliyun.com/document_detail/163515.htm?spm=a2c4g.11186623.0.0.44d65c58mhgNw3 13 | # namespaces=["acs_ecs_dashboard"] 14 | # [[instances.metric_filters]] 15 | # # metric name 参考 https://help.aliyun.com/document_detail/163515.htm?spm=a2c4g.11186623.0.0.401d15c73Z0dZh 16 | # # 参考页面中的Metric Id 填入下面的metricName ,页面中包含中文的Metric Name对应接口中的Description 17 | # metric_names=["cpu_cores","vm.TcpCount"] 18 | # namespace="" 19 | # ratelimit=25 20 | # catch_ttl="1h" 21 | # timeout="5s" 22 | -------------------------------------------------------------------------------- /integrations/AliYun/icon/aliyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AliYun/icon/aliyun.png -------------------------------------------------------------------------------- /integrations/AliYun/markdown/ecs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AliYun/markdown/ecs.png -------------------------------------------------------------------------------- /integrations/AliYun/markdown/rds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AliYun/markdown/rds.png -------------------------------------------------------------------------------- /integrations/AliYun/markdown/redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AliYun/markdown/redis.png -------------------------------------------------------------------------------- /integrations/AliYun/markdown/slb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AliYun/markdown/slb.png -------------------------------------------------------------------------------- /integrations/AliYun/markdown/waf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AliYun/markdown/waf.png -------------------------------------------------------------------------------- /integrations/AppDynamics/collect/appdynamics/app.toml: -------------------------------------------------------------------------------- 1 | #interval=15s 2 | 3 | [[instances]] 4 | #url_base = "http://{{.ip}}:{{.port}}/a.json?metric-path={{.metric_path}}&time-range-type=BETWEEN_TIMES&start-time={{.start_time}}&end-time={{.end_time}}&output=JSON" 5 | #url_vars = [ 6 | # { ip="127.0.0.1", port="8090", application="cms", metric_path="Application Infrastructure Performance|AdminServer|Individual Nodes|xxxxx|Agent|App|Availability", start_time="$START_TIME", end_time="$END_TIME"}, 7 | #] 8 | 9 | # # 指定url_vars中哪些key 作为最终的label附加 10 | # url_var_label_keys= [] 11 | 12 | # #从url中提取变量 13 | # url_label_key="instance" 14 | # url_label_value="{{.Host}}" 15 | # #自定义 http header 16 | #headers = { Authorization="", X-Forwarded-For="", Host=""} 17 | # #每次请求的超时时间 18 | #timeout="5s" 19 | 20 | # # precision of start-time and end-time 21 | #precision="ms" 22 | 23 | ## basic auth 24 | #username="" 25 | #password="" 26 | 27 | # # endtime = now - delay 28 | #delay = "1m" 29 | # # starttime = now - delay - period = endtime - period 30 | #period = "1m" 31 | 32 | # # 想要添加的额外label 33 | #labels = {application="cms"} 34 | # # 从返回中过滤哪些指标 35 | filters = ["current", "max", "min", "value","sum", "count"] 36 | 37 | # # 限制并发请求量, 最多同时有多少个请求 38 | # # 默认范围(0,100) 39 | #request_inflight= 10 40 | ## 强制开启100以上的并发请求 (不推荐) 41 | # force_request_inflight = 1000 42 | 43 | # # 是否开启 tls 44 | # use_tls = true 45 | # # tls 最小版本 46 | ## tls_min_version = "1.2" 47 | # # tls ca证书路径 48 | ## tls_ca = "/etc/categraf/ca.pem" 49 | # # tls cert 路径 50 | ## tls_cert = "/etc/categraf/cert.pem" 51 | # # tls key 路径 52 | ## tls_key = "/etc/categraf/key.pem" 53 | # # 是否跳过证书验证 54 | ## insecure_skip_verify = true 55 | -------------------------------------------------------------------------------- /integrations/AppDynamics/icon/appdynamics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AppDynamics/icon/appdynamics.png -------------------------------------------------------------------------------- /integrations/AutoMQ/collect/prometheus/采集OTEL-COLLECTOR的样例.toml: -------------------------------------------------------------------------------- 1 | interval = 15 2 | 3 | [[instances]] 4 | urls = [ 5 | "http://:/metrics" 6 | ] 7 | 8 | url_label_key = "otel_collector" 9 | url_label_value = "{{.Host}}" -------------------------------------------------------------------------------- /integrations/AutoMQ/icon/automq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/AutoMQ/icon/automq.png -------------------------------------------------------------------------------- /integrations/AutoMQ/markdown/overview.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | AuthMQ 官方文档提供了指标吐出方式以及和监控系统的整合方式,具体可以参考[AutoMQ](https://docs.automq.com/zh/docs/automq-opensource/LkwkwdQlwizjqckhj0dcc2IdnDh)。 4 | 5 | ## 推荐方式 6 | 7 | 建议采用 AutoMQ 文档中的方案二:使用 Prometheus OTLP Receiver 的方式,把所有的指标都收集到 OTel Collector 中,然后使用 Prometheus 或者 Categraf 直接去拉取数据即可。假如使用 Categraf,就是使用 prometheus 插件去拉取数据,比如我们为 prometheus 插件提供一个单独的 automq.toml 的配置文件:`conf/input.prometheus/automq.toml` ,内容如下: 8 | 9 | ```toml 10 | [[instances]] 11 | urls = [ 12 | "http://:/metrics" 13 | ] 14 | 15 | url_label_key = "otel_collector" 16 | url_label_value = "{{.Host}}" 17 | ``` 18 | 19 | 注意,url_label_key 一般都是指定为 instance,但是这里故意指定为其他字符串,是因为 AutoMQ 原始的指标中包含了 instance 标签,为了避免冲突,所以指定为其他字符串。 20 | 21 | -------------------------------------------------------------------------------- /integrations/Bind/collect/bind/bind.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | urls = [ 3 | # "http://localhost:8053/xml/v3", 4 | ] 5 | gather_memory_contexts = true 6 | gather_views = true 7 | timeout = "5s" 8 | # labels={app="bind"} -------------------------------------------------------------------------------- /integrations/Bind/icon/bind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Bind/icon/bind.png -------------------------------------------------------------------------------- /integrations/Bind/markdown/README.md: -------------------------------------------------------------------------------- 1 | forked from [telegraf/snmp](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/bind) 2 | 3 | 配置示例 4 | ``` 5 | [[instances]] 6 | urls = [ 7 | #"http://localhost:8053/xml/v3", 8 | ] 9 | 10 | timeout = "5s" 11 | gather_memory_contexts = true 12 | gather_views = true 13 | ``` -------------------------------------------------------------------------------- /integrations/Canal/icon/canal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Canal/icon/canal.png -------------------------------------------------------------------------------- /integrations/Canal/markdown/README.md: -------------------------------------------------------------------------------- 1 | ## canal 2 | 3 | canal 默认提供了 prometheus 格式指标的接口 [Prometheus-QuickStart](https://github.com/alibaba/canal/wiki/Prometheus-QuickStart) ,所以可以直接通过[ prometheus 插件](https://flashcat.cloud/docs/content/flashcat-monitor/categraf/plugin/prometheus)采集。 -------------------------------------------------------------------------------- /integrations/Ceph/icon/ceph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ceph/icon/ceph.png -------------------------------------------------------------------------------- /integrations/Ceph/markdown/README.md: -------------------------------------------------------------------------------- 1 | # ceph plugin 2 | 3 | 开启 ceph prometheus 支持 4 | 5 | ```bash 6 | ceph mgr module enable prometheus 7 | ``` 8 | 9 | ## 采集配置 10 | 11 | 既然 ceph 可以暴露 prometheus 协议的 metrics 数据,则直接使用 prometheus 插件抓取即可。 12 | 13 | categraf 配置文件:`conf/input.prometheus/prometheus.toml` 14 | 15 | ```yaml 16 | [[instances]] 17 | urls = [ 18 | "http://192.168.11.181:9283/metrics" 19 | ] 20 | labels = {service="ceph",cluster="ceph-cluster-001"} 21 | ``` 22 | 23 | 24 | ## 仪表盘效果 25 | 26 | 夜莺内置仪表盘中已经内置了 ceph 的仪表盘,导入即可使用。 27 | 28 | ![20230801152445](https://download.flashcat.cloud/ulric/20230801152445.png) 29 | 30 | ## 告警规则 31 | 32 | 夜莺内置告警规则中已经内置了 ceph 的告警规则,导入即可使用。 33 | 34 | ![20230801152431](https://download.flashcat.cloud/ulric/20230801152431.png) -------------------------------------------------------------------------------- /integrations/Ceph/markdown/alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ceph/markdown/alerts.png -------------------------------------------------------------------------------- /integrations/Ceph/markdown/ceph-alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ceph/markdown/ceph-alerts.png -------------------------------------------------------------------------------- /integrations/Ceph/markdown/ceph-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ceph/markdown/ceph-dash.png -------------------------------------------------------------------------------- /integrations/Ceph/markdown/ceph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ceph/markdown/ceph.png -------------------------------------------------------------------------------- /integrations/ClickHouse/icon/clickhouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/ClickHouse/icon/clickhouse.png -------------------------------------------------------------------------------- /integrations/CloudWatch/icon/cloudwatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/CloudWatch/icon/cloudwatch.png -------------------------------------------------------------------------------- /integrations/Consul/collect/consul/consul.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | ## Consul server address 6 | # address = "localhost:8500" 7 | 8 | ## URI scheme for the Consul server, one of "http", "https" 9 | # scheme = "http" 10 | 11 | ## ACL token used in every request 12 | # token = "" 13 | 14 | ## HTTP Basic Authentication username and password. 15 | # username = "" 16 | # password = "" 17 | 18 | ## Data center to query the health checks from 19 | # datacenter = "" 20 | 21 | ## Allows any Consul server (non-leader) to service a read. 22 | ## Default is true 23 | # allow_stale = true 24 | 25 | ## Forces the read to be fully consistent. 26 | ## Default is false 27 | # require_consistent = false 28 | 29 | ## Prefix from which to expose key/value pairs. 30 | # kv_prefix = "" 31 | 32 | ## Regex that determines which keys to expose. 33 | ## Default is ".*" 34 | # kv_filter = ".*" 35 | 36 | ## Optional TLS Config 37 | # tls_ca = "/etc/telegraf/ca.pem" 38 | # tls_cert = "/etc/telegraf/cert.pem" 39 | # tls_key = "/etc/telegraf/key.pem" 40 | ## Use TLS but skip chain & host verification 41 | # insecure_skip_verify = true 42 | -------------------------------------------------------------------------------- /integrations/Consul/icon/consul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Consul/icon/consul.png -------------------------------------------------------------------------------- /integrations/Dns_Query/collect/dns_query/dns_query.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # # append some labels for series 6 | # labels = { region="cloud", product="n9e" } 7 | 8 | # # interval = global.interval * interval_times 9 | # interval_times = 1 10 | 11 | # # 12 | auto_detect_local_dns_server = false 13 | 14 | ## servers to query 15 | # servers = ["8.8.8.8"] 16 | servers = [] 17 | 18 | ## Network is the network protocol name. 19 | # network = "udp" 20 | 21 | ## Domains or subdomains to query. 22 | # domains = ["."] 23 | 24 | ## Query record type. 25 | ## Possible values: A, AAAA, CNAME, MX, NS, PTR, TXT, SOA, SPF, SRV. 26 | # record_type = "A" 27 | 28 | ## Dns server port. 29 | # port = 53 30 | 31 | ## Query timeout in seconds. 32 | # timeout = 2 -------------------------------------------------------------------------------- /integrations/Dns_Query/icon/dns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Dns_Query/icon/dns.png -------------------------------------------------------------------------------- /integrations/Docker/icon/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Docker/icon/docker.png -------------------------------------------------------------------------------- /integrations/Docker/markdown/README.md: -------------------------------------------------------------------------------- 1 | # docker 2 | 3 | forked from telegraf/inputs.docker 4 | 5 | ## change 6 | 7 | 1. Using `container_id` as label not field 8 | 1. Some metrics have been deleted 9 | 10 | ## 容器ID标签 11 | 12 | 通过下面两个配置来控制 container_id 这个标签: 13 | 14 | ```ini 15 | container_id_label_enable = true 16 | container_id_label_short_style = false 17 | ``` 18 | 19 | 默认 container_id_label_enable 设置为 true,表示启用,即会把容器ID放到标签里,container_id_label_short_style 是短格式,容器ID很长,如果把 short_style 设置为 true,就会只截取前面12位 20 | 21 | ## 权限问题 22 | 23 | Categraf 最好是用 root 账号来运行,否则,请求 docker.sock 可能会遇到权限问题,需要把 Categraf 的运行账号,加到 docker group 中,假设 Categraf 使用 categraf 账号运行: 24 | 25 | ``` 26 | sudo usermod -aG docker categraf 27 | ``` 28 | 29 | ## 运行在容器里 30 | 31 | 如果 Categraf 运行在容器中,docker 的 unix socket 就需要挂到 Categraf 的容器里,比如通过 `-v /var/run/docker.sock:/var/run/docker.sock` 这样的参数来启动 Categraf 的容器。如果是在 compose 环境下,也可以在 docker compose 配置中加上 volume 的配置: 32 | 33 | ```yaml 34 | volumes: 35 | - /var/run/docker.sock:/var/run/docker.sock 36 | ``` 37 | 38 | ## 停用该插件 39 | 40 | - 方法一:把 `input.docker` 目录改个别的名字,不用 `input.` 打头 41 | - 方法二:docker.toml 中的 endpoint 配置留空 -------------------------------------------------------------------------------- /integrations/Doris/collect/prometheus/collect_doris_examples.toml: -------------------------------------------------------------------------------- 1 | # doris_fe 2 | [[instances]] 3 | # 配置 fe metrics 服务地址 4 | urls = [ 5 | "http://127.0.0.1:8030/metrics" 6 | ] 7 | 8 | url_label_key = "instance" 9 | url_label_value = "{{.Host}}" 10 | # 指定 fe 服务 group 和 job 标签,这里是仪表盘变量调用,可根据实际需求修改。 11 | labels = { group = "fe",job = "doris_cluster01"} 12 | 13 | # doris_be 14 | [[instances]] 15 | # 配置 be metrics 服务地址 16 | urls = [ 17 | "http://127.0.0.1:8040/metrics" 18 | ] 19 | url_label_key = "instance" 20 | url_label_value = "{{.Host}}" 21 | # 指定 be 服务 group 和 job 标签,这里是仪表盘变量调用,可根据实际需求修改。 22 | labels = { group = "be",job = "doris_cluster01"} -------------------------------------------------------------------------------- /integrations/Doris/icon/doris.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 13 | 16 | 20 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /integrations/Doris/markdown/README.md: -------------------------------------------------------------------------------- 1 | # Doris 2 | 3 | Doris 的进程都会暴露 `/metrics` 接口,通过这个接口暴露 Prometheus 协议的监控数据。 4 | 5 | ## 采集配置 6 | 7 | categraf 的 `conf/input.prometheus/prometheus.toml`。因为 Doris 是暴露的 Prometheus 协议的监控数据,所以使用 categraf 的 prometheus 插件即可采集。 8 | 9 | ```toml 10 | # doris_fe 11 | [[instances]] 12 | urls = [ 13 | "http://127.0.0.1:8030/metrics" 14 | ] 15 | 16 | url_label_key = "instance" 17 | url_label_value = "{{.Host}}" 18 | 19 | labels = { group = "fe",job = "doris_cluster01"} 20 | 21 | # doris_be 22 | [[instances]] 23 | urls = [ 24 | "http://127.0.0.1:8040/metrics" 25 | ] 26 | url_label_key = "instance" 27 | url_label_value = "{{.Host}}" 28 | labels = { group = "be",job = "doris_cluster01"} 29 | ``` 30 | 31 | ## 告警规则 32 | 33 | 夜莺内置了 Doris 的告警规则,克隆到自己的业务组下即可使用。 34 | 35 | ## 仪表盘 36 | 37 | 夜莺内置了 Doris 的仪表盘,克隆到自己的业务组下即可使用。 38 | 39 | 40 | -------------------------------------------------------------------------------- /integrations/Elasticsearch/icon/elasticsearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Elasticsearch/icon/elasticsearch.png -------------------------------------------------------------------------------- /integrations/Elasticsearch/markdown/README.md: -------------------------------------------------------------------------------- 1 | # elasticsearch plugin 2 | 3 | ElasticSearch 通过 HTTP JSON 的方式暴露了自身的监控指标,通过 categraf 的 [elasticsearch](https://github.com/flashcatcloud/categraf/tree/main/inputs/elasticsearch) 插件抓取。 4 | 5 | 如果是小规模集群,设置 `local=false`,从集群中某一个节点抓取数据,即可拿到整个集群所有节点的监控数据。如果是大规模集群,建议设置 `local=true`,在集群的每个节点上都部署抓取器,抓取本地 elasticsearch 进程的监控数据。 6 | 7 | 8 | ## 配置示例 9 | 10 | categraf 配置文件:`conf/input.elasticsearch/elasticsearch.toml` 11 | 12 | ```yaml 13 | [[instances]] 14 | servers = ["http://192.168.11.177:9200"] 15 | http_timeout = "10s" 16 | local = false 17 | cluster_health = true 18 | cluster_health_level = "cluster" 19 | cluster_stats = true 20 | indices_level = "" 21 | node_stats = ["jvm", "breaker", "process", "os", "fs", "indices", "thread_pool", "transport"] 22 | username = "elastic" 23 | password = "xxxxxxxx" 24 | num_most_recent_indices = 1 25 | labels = { service="es" } 26 | ``` 27 | 28 | ## 仪表盘效果 29 | 30 | 夜莺内置仪表盘中已经内置了 Elasticsearch 的仪表盘,导入即可使用。 31 | 32 | ![](http://download.flashcat.cloud/uPic/es-dashboard.jpeg) -------------------------------------------------------------------------------- /integrations/Elasticsearch/markdown/es-dashboard.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Elasticsearch/markdown/es-dashboard.jpeg -------------------------------------------------------------------------------- /integrations/Exec/collect/exec/exec.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # # commands, support glob 6 | commands = [ 7 | # "/opt/categraf/scripts/*.sh" 8 | ] 9 | 10 | # # timeout for each command to complete 11 | # timeout = 5 12 | 13 | # # interval = global.interval * interval_times 14 | # interval_times = 1 15 | 16 | # # choices: influx prometheus falcon 17 | # # influx stdout example: mesurement,labelkey1=labelval1,labelkey2=labelval2 field1=1.2,field2=2.3 18 | # data_format = "influx" 19 | -------------------------------------------------------------------------------- /integrations/Exec/icon/exec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Exec/icon/exec.png -------------------------------------------------------------------------------- /integrations/Filecount/collect/filecount/filecount.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # # append some labels for series 6 | # labels = { region="cloud", product="n9e" } 7 | 8 | # # interval = global.interval * interval_times 9 | # interval_times = 1 10 | 11 | ## Directories to gather stats about. 12 | ## This accept standard unit glob matching rules, but with the addition of 13 | ## ** as a "super asterisk". ie: 14 | ## /var/log/** -> recursively find all directories in /var/log and count files in each directories 15 | ## /var/log/*/* -> find all directories with a parent dir in /var/log and count files in each directories 16 | ## /var/log -> count all files in /var/log and all of its subdirectories 17 | ## directories = ["/var/cache/apt", "/tmp"] 18 | directories = ["/tmp"] 19 | 20 | ## Only count files that match the name pattern. Defaults to "*". 21 | file_name = "*" 22 | 23 | ## Count files in subdirectories. Defaults to true. 24 | recursive = true 25 | 26 | ## Only count regular files. Defaults to true. 27 | regular_only = true 28 | 29 | ## Follow all symlinks while walking the directory tree. Defaults to false. 30 | follow_symlinks = false 31 | 32 | ## Only count files that are at least this size. If size is 33 | ## a negative number, only count files that are smaller than the 34 | ## absolute value of size. Acceptable units are B, KiB, MiB, KB, ... 35 | ## Without quotes and units, interpreted as size in bytes. 36 | size = "0B" 37 | 38 | ## Only count files that have not been touched for at least this 39 | ## duration. If mtime is negative, only count files that have been 40 | ## touched in this duration. Defaults to "0s". 41 | mtime = "0s" 42 | -------------------------------------------------------------------------------- /integrations/Filecount/icon/filecount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Filecount/icon/filecount.png -------------------------------------------------------------------------------- /integrations/Gitlab/icon/gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Gitlab/icon/gitlab.png -------------------------------------------------------------------------------- /integrations/GoogleCloud/collect/googlecloud/gcp.toml: -------------------------------------------------------------------------------- 1 | #interval=60 2 | #[[instances]] 3 | #project_id="your-project-id" 4 | #credentials_file="/path/to/your/key.json" 5 | #delay="2m" 6 | #period="1m" 7 | #filter="metric.type=\"compute.googleapis.com/instance/cpu/utilization\" AND resource.labels.zone=\"asia-northeast1-a\"" 8 | #timeout="5s" 9 | #cache_ttl="1h" 10 | #gce_host_tag="xxx" 11 | #request_inflight=30 12 | -------------------------------------------------------------------------------- /integrations/GoogleCloud/icon/gcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/GoogleCloud/icon/gcp.png -------------------------------------------------------------------------------- /integrations/GoogleCloud/markdown/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # GCP 指标获取插件 4 | 5 | ## 需要权限 6 | ```shell 7 | https://www.googleapis.com/auth/monitoring.read 8 | ``` 9 | 10 | ## 配置 11 | ```toml 12 | #采集周期,建议 >= 1分钟 13 | interval=60 14 | [[instances]] 15 | #配置 project_id 16 | project_id="your-project-id" 17 | #配置认证的key文件 18 | credentials_file="/path/to/your/key.json" 19 | #或者配置认证的JSON 20 | credentials_json="xxx" 21 | 22 | # 指标的end time = now - delay 23 | #delay="2m" 24 | # 指标的start time = now - deley - period 25 | #period="1m" 26 | # 过滤器 27 | #filter="metric.type=\"compute.googleapis.com/instance/cpu/utilization\" AND resource.labels.zone=\"asia-northeast1-a\"" 28 | # 请求超时时间 29 | #timeout="5s" 30 | # 指标列表的缓存时长 ,filter为空时 启用 31 | #cache_ttl="1h" 32 | 33 | # 给gce的instance_name 取个别名,放到label中 34 | #gce_host_tag="xxx" 35 | # 每次最多有多少请求同时发起 36 | #request_inflight=30 37 | 38 | # request_inflight 取值(0,100] 39 | # 想配置更大的值 ,前提是你知道你在做什么 40 | force_request_inflight= 200 41 | ``` 42 | -------------------------------------------------------------------------------- /integrations/HAProxy/collect/haproxy/haproxy.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | # URI on which to scrape HAProxy. 3 | # e.g. 4 | # uri = "http://localhost:5000/baz?stats;csv" 5 | # uri = "http://user:pass@haproxy.example.com/haproxy?stats;csv" 6 | # uri = "unix:/run/haproxy/admin.sock" 7 | uri = "" 8 | 9 | # Flag that enables SSL certificate verification for the scrape URI 10 | ssl_verify = false 11 | 12 | # Comma-separated list of exported server metrics. See http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#9.1 13 | server_metric_fields = "" 14 | 15 | # Comma-separated list of exported server states to exclude. See https://cbonte.github.io/haproxy-dconv/1.8/management.html#9.1, field 17 status 16 | server_exclude_states = "" 17 | 18 | # Timeout for trying to get stats from HAProxy. 19 | timeout = "5s" 20 | 21 | # Flag that enables using HTTP proxy settings from environment variables ($http_proxy, $https_proxy, $no_proxy) 22 | proxy_from_env = false 23 | -------------------------------------------------------------------------------- /integrations/HAProxy/icon/haproxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/HAProxy/icon/haproxy.png -------------------------------------------------------------------------------- /integrations/HAProxy/markdown/README.md: -------------------------------------------------------------------------------- 1 | # HAProxy 2 | 3 | forked from [haproxy_exporter](https://github.com/prometheus/haproxy_exporter) 4 | 5 | Note: since HAProxy 2.0.0, the official source includes a Prometheus exporter module that can be built into your binary with a single flag during build time and offers an exporter-free Prometheus endpoint. 6 | 7 | 8 | haproxy configurations for `/stats`: 9 | 10 | ``` 11 | frontend stats 12 | bind *:8404 13 | stats enable 14 | stats uri /stats 15 | stats refresh 10s 16 | ``` -------------------------------------------------------------------------------- /integrations/HTTP_Response/icon/http_response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/HTTP_Response/icon/http_response.png -------------------------------------------------------------------------------- /integrations/IPMI/collect/ipmi/conf.toml: -------------------------------------------------------------------------------- 1 | # Read metrics from the bare metal servers via freeipmi 2 | [[instances]] 3 | # target指定是本地采集还是远程采集 4 | #target="localhost" 5 | # 指定采集的用户名和密码,这里务必保证ipmi命令能获取正确输出,不是网上查到一个用户名 密码就可以。 6 | #user = "user" 7 | #pass = "1234" 8 | 9 | # ipmi协议版本,支持1.5 和 2.0 10 | #driver = "LAN_2_0" 11 | 12 | # 指定特权用户名 13 | #privilege = "user" 14 | 15 | ## session-timeout, ms 16 | #timeout = 100000 17 | 18 | # 支持的采集器 bmc, bmc-watchdog, ipmi, chassis, dcmi, sel,sm-lan-mode 19 | # 默认使用 bmc, ipmi, chassis和dcmi,建议保持下列配置便于仪表盘更好的展示 20 | collectors = [ "bmc", "ipmi", "chassis", "sel", "dcmi"] 21 | 22 | # 不关注的传感器,指定id 排除掉 23 | #exclude_sensor_ids = [ 2, 29, 32, 50, 52, 55 ] 24 | 25 | # 如果你想使用定制化的参数覆盖内置的命令,可以修改以下内容; 建议保持注释 26 | #[instances.collector_cmd] 27 | #ipmi = "sudo" 28 | #sel = "sudo" 29 | #[instances.default_args] 30 | #ipmi = [ "--bridge-sensors" ] 31 | #[instances.custom_args] 32 | #ipmi = [ "--bridge-sensors" ] 33 | #sel = [ "ipmi-sel" ] -------------------------------------------------------------------------------- /integrations/IPMI/icon/ipmi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/IPMI/icon/ipmi.png -------------------------------------------------------------------------------- /integrations/IPMI/markdown/README.md: -------------------------------------------------------------------------------- 1 | # IPMI plugin 2 | ipmi插件是从ipmi exporter迁移过来。 基本原理是通过执行ipmi的一系列命令并将命令输出转换为指标,如果ipmi没有配置好,是无法采集到指标的,请务必将ipmi配置好。 3 | 4 | categraf的ipmi插件配置举例如下: 5 | ```toml 6 | # Read metrics from the bare metal servers via freeipmi 7 | [[instances]] 8 | # target指定是本地采集还是远程采集 9 | #target="localhost" 10 | # 指定采集的用户名和密码,这里务必保证ipmi命令能获取正确输出,不是网上查到一个用户名 密码就可以。 11 | #user = "user" 12 | #pass = "1234" 13 | 14 | # ipmi协议版本,支持1.5 和 2.0 15 | #driver = "LAN_2_0" 16 | 17 | # 指定特权用户名 18 | #privilege = "user" 19 | 20 | ## session-timeout, ms 21 | #timeout = 100000 22 | 23 | # 支持的采集器 bmc, bmc-watchdog, ipmi, chassis, dcmi, sel,sm-lan-mode 24 | # 默认使用 bmc, ipmi, chassis和dcmi,建议保持下列配置便于仪表盘更好的展示 25 | collectors = [ "bmc", "ipmi", "chassis", "sel", "dcmi"] 26 | 27 | # 不关注的传感器,指定id 排除掉 28 | #exclude_sensor_ids = [ 2, 29, 32, 50, 52, 55 ] 29 | 30 | # 如果你想使用定制化的参数覆盖内置的命令,可以修改以下内容; 建议保持注释 31 | #[instances.collector_cmd] 32 | #ipmi = "sudo" 33 | #sel = "sudo" 34 | #[instances.default_args] 35 | #ipmi = [ "--bridge-sensors" ] 36 | #[instances.custom_args] 37 | #ipmi = [ "--bridge-sensors" ] 38 | #sel = [ "ipmi-sel" ] 39 | ``` -------------------------------------------------------------------------------- /integrations/IPVS/collect/ipvs/ipvs.toml: -------------------------------------------------------------------------------- 1 | # Collect virtual and real server stats from Linux IPVS 2 | # no configuration 3 | -------------------------------------------------------------------------------- /integrations/IPVS/icon/ipvs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/IPVS/icon/ipvs.png -------------------------------------------------------------------------------- /integrations/JMX/icon/jmx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/JMX/icon/jmx.png -------------------------------------------------------------------------------- /integrations/Jenkins/collect/jenkins/jenkins.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # Address (host:port) of jenkins server. 6 | # jenkins_url = "http://my-jenkins-instance:8080" 7 | 8 | #jenkins_username = "admin" 9 | #jenkins_password = "" 10 | 11 | #response_timeout = "5s" 12 | 13 | -------------------------------------------------------------------------------- /integrations/Jenkins/icon/jenkins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Jenkins/icon/jenkins.png -------------------------------------------------------------------------------- /integrations/Jenkins/markdown/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins 2 | 3 | Jenkins 采集插件, 采集 Jenkins 数据 4 | 5 | ## Configuration 6 | 7 | ```toml 8 | # # collect interval 9 | # interval = 15 10 | 11 | [[instances]] 12 | # Address (host:port) of jenkins server. 13 | # jenkins_url = "http://my-jenkins-instance:8080" 14 | 15 | #jenkins_username = "admin" 16 | #jenkins_password = "" 17 | 18 | #response_timeout = "5s" 19 | 20 | 21 | ``` -------------------------------------------------------------------------------- /integrations/Jolokia_Agent/collect/jolokia_agent/bitbucket.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | urls = ["http://localhost:8778/jolokia"] 3 | metrics_name_prefix = "bitbucket_" 4 | 5 | [[instances.metric]] 6 | name = "jvm_operatingsystem" 7 | mbean = "java.lang:type=OperatingSystem" 8 | 9 | [[instances.metric]] 10 | name = "jvm_runtime" 11 | mbean = "java.lang:type=Runtime" 12 | 13 | [[instances.metric]] 14 | name = "jvm_thread" 15 | mbean = "java.lang:type=Threading" 16 | 17 | [[instances.metric]] 18 | name = "jvm_memory" 19 | mbean = "java.lang:type=Memory" 20 | 21 | [[instances.metric]] 22 | name = "jvm_class_loading" 23 | mbean = "java.lang:type=ClassLoading" 24 | 25 | [[instances.metric]] 26 | name = "jvm_memory_pool" 27 | mbean = "java.lang:type=MemoryPool,name=*" 28 | 29 | [[instances.metric]] 30 | name = "webhooks" 31 | mbean = "com.atlassian.webhooks:name=*" 32 | 33 | [[instances.metric]] 34 | name = "atlassian" 35 | mbean = "com.atlassian.bitbucket:name=*" 36 | 37 | [[instances.metric]] 38 | name = "thread_pools" 39 | mbean = "com.atlassian.bitbucket.thread-pools:name=*" 40 | -------------------------------------------------------------------------------- /integrations/Jolokia_Agent/collect/jolokia_agent/java.toml: -------------------------------------------------------------------------------- 1 | 2 | [[instances]] 3 | urls = ["http://localhost:8080/jolokia"] 4 | 5 | [[instances.metric]] 6 | name = "java_runtime" 7 | mbean = "java.lang:type=Runtime" 8 | paths = ["Uptime"] 9 | 10 | [[instances.metric]] 11 | name = "java_memory" 12 | mbean = "java.lang:type=Memory" 13 | paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"] 14 | 15 | [[instances.metric]] 16 | name = "java_garbage_collector" 17 | mbean = "java.lang:name=*,type=GarbageCollector" 18 | paths = ["CollectionTime", "CollectionCount"] 19 | tag_keys = ["name"] 20 | 21 | [[instances.metric]] 22 | name = "java_last_garbage_collection" 23 | mbean = "java.lang:name=G1 Young Generation,type=GarbageCollector" 24 | paths = ["LastGcInfo/duration", "LastGcInfo/GcThreadCount", "LastGcInfo/memoryUsageAfterGc"] 25 | 26 | [[instances.metric]] 27 | name = "java_threading" 28 | mbean = "java.lang:type=Threading" 29 | paths = ["TotalStartedThreadCount", "ThreadCount", "DaemonThreadCount", "PeakThreadCount"] 30 | 31 | [[instances.metric]] 32 | name = "java_class_loading" 33 | mbean = "java.lang:type=ClassLoading" 34 | paths = ["LoadedClassCount", "UnloadedClassCount", "TotalLoadedClassCount"] 35 | 36 | [[instances.metric]] 37 | name = "java_memory_pool" 38 | mbean = "java.lang:name=*,type=MemoryPool" 39 | paths = ["Usage", "PeakUsage", "CollectionUsage"] 40 | tag_keys = ["name"] 41 | -------------------------------------------------------------------------------- /integrations/Jolokia_Agent/collect/jolokia_agent/zookeeper.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | urls = ["http://localhost:8080/jolokia"] 3 | name_prefix = "zk_" 4 | 5 | [[instances.metric]] 6 | name = "quorum" 7 | mbean = "org.apache.ZooKeeperService:name0=*" 8 | tag_keys = ["name0"] 9 | 10 | [[instances.metric]] 11 | name = "leader" 12 | mbean = "org.apache.ZooKeeperService:name0=*,name1=*,name2=Leader" 13 | tag_keys = ["name1"] 14 | 15 | [[instances.metric]] 16 | name = "follower" 17 | mbean = "org.apache.ZooKeeperService:name0=*,name1=*,name2=Follower" 18 | tag_keys = ["name1"] 19 | -------------------------------------------------------------------------------- /integrations/Jolokia_Agent/icon/jolokia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Jolokia_Agent/icon/jolokia.png -------------------------------------------------------------------------------- /integrations/Jolokia_Agent/markdown/README.md: -------------------------------------------------------------------------------- 1 | # Jolokia Agent 2 | 3 | forked from telegraf/inputs.jolokia2_agent 4 | 5 | ## 停用该插件 6 | 7 | - 方法一:把 `input.jolokia_agent_misc` 目录改个别的名字,不用 `input.` 打头 8 | - 方法二:xx.toml 中的配置留空 -------------------------------------------------------------------------------- /integrations/Kafka/icon/kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Kafka/icon/kafka.png -------------------------------------------------------------------------------- /integrations/Kafka/markdown/alerts-kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Kafka/markdown/alerts-kafka.png -------------------------------------------------------------------------------- /integrations/Kafka/markdown/alerts..png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Kafka/markdown/alerts..png -------------------------------------------------------------------------------- /integrations/Kafka/markdown/dash-kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Kafka/markdown/dash-kafka.png -------------------------------------------------------------------------------- /integrations/Kafka/markdown/dashboards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Kafka/markdown/dashboards.png -------------------------------------------------------------------------------- /integrations/Kubernetes/icon/kubernetes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Kubernetes/icon/kubernetes.png -------------------------------------------------------------------------------- /integrations/Kubernetes/markdown/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 2 | 3 | 这个插件已经废弃。Kubernetes 监控系列可以参考这个 [文章](https://flashcat.cloud/categories/kubernetes%E7%9B%91%E6%8E%A7%E4%B8%93%E6%A0%8F/)。 4 | 5 | 不过 Kubernetes 这个类别下的内置告警规则和内置仪表盘都是可以使用的。 6 | 7 | --- 8 | 9 | 下面是老插件文档: 10 | 11 | forked from telegraf/kubernetes. 这个插件的作用是通过kubelet提供的API获取监控数据,包括系统容器的监控数据、node的、pod数据卷的、pod网络的、pod容器的。 12 | 13 | ## Change 14 | 15 | 增加了一些控制开关: 16 | 17 | `gather_system_container_metrics = true` 18 | 19 | 是否采集 system 容器(kubelet、runtime、misc、pods),比如 kubelet 一般就是静态容器,非业务容器 20 | 21 | `gather_node_metrics = true` 22 | 23 | 是否采集 node 层面的指标,机器层面的指标其实 categraf 来采集了,这里理论上不需要再采集了,可以设置为 false,采集也没问题,也没多少数据 24 | 25 | `gather_pod_container_metrics = true` 26 | 27 | 是否采集 Pod 中的容器的指标,这些 Pod 一般是业务容器 28 | 29 | `gather_pod_volume_metrics = true` 30 | 31 | 是否采集 Pod 的数据卷的指标 32 | 33 | `gather_pod_network_metrics = true` 34 | 35 | 是否采集 Pod 的网络监控数据 36 | 37 | ## 容器监控 38 | 39 | 通过这些开关可以看出,kubernetes 这个插件,采集的只是 pod、容器的监控指标,这些指标数据来自 kubelet 的 `/stats/summary` `/pods` 等接口。那么问题来了,容器监控到底是应该读取 `/metrics/cadvisor` 接口还是应该用这个 kubernetes 插件?有几个决策依据: 40 | 41 | 1. `/metrics/cadvisor` 采集的数据没有业务自定义标签,kubernetes 这个插件会自动带上业务自定义标签。但是业务标签可能比较混乱,建议每个公司制定规范,比如要求业务只能打 project、region、env、service、app、job 等标签,其他标签都过滤掉,通过 kubernetes 插件的 label_include label_exclude 配置,可以做标签过滤。 42 | 2. kubernetes 这个插件采集的数据比 `/metrics/cadvisor` 吐出的指标要少,不过常见的 cpu、mem、net、volume 相关的也都有。 43 | -------------------------------------------------------------------------------- /integrations/Ldap/collect/ldap/ldap.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # # append some labels for series 6 | # labels = { region="cloud", product="n9e" } 7 | 8 | # # interval = global.interval * interval_times 9 | # interval_times = 1 10 | 11 | ## Server to monitor 12 | ## The scheme determines the mode to use for connection with 13 | ## ldap://... -- unencrypted (non-TLS) connection 14 | ## ldaps://... -- TLS connection 15 | ## starttls://... -- StartTLS connection 16 | ## If no port is given, the default ports, 389 for ldap and starttls and 17 | ## 636 for ldaps, are used. 18 | #server = "ldap://localhost" 19 | 20 | ## Server dialect, can be "openldap" or "389ds" 21 | # dialect = "openldap" 22 | 23 | # DN and password to bind with 24 | ## If bind_dn is empty an anonymous bind is performed. 25 | bind_dn = "" 26 | bind_password = "" 27 | 28 | ## Reverse the field names constructed from the monitoring DN 29 | # reverse_field_names = false 30 | 31 | ## Optional TLS Config 32 | # use_tls = false 33 | # tls_ca = "/etc/categraf/ca.pem" 34 | # tls_cert = "/etc/categraf/cert.pem" 35 | # tls_key = "/etc/categraf/key.pem" 36 | ## Use TLS but skip chain & host verification 37 | # insecure_skip_verify = false -------------------------------------------------------------------------------- /integrations/Ldap/icon/ldap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ldap/icon/ldap.png -------------------------------------------------------------------------------- /integrations/Linux/collect/arp_packet/arp_packet.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | #eth_device="ens192" -------------------------------------------------------------------------------- /integrations/Linux/collect/netstat/netstat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | disable_summary_stats = false 5 | ## if machine has many network connections, use this plugin may exhaust your cpu resource, diable connection stat to avoid this 6 | disable_connection_stats = true 7 | 8 | tcp_ext = false 9 | ip_ext = false 10 | -------------------------------------------------------------------------------- /integrations/Linux/collect/ntp/ntp.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # ntp servers 5 | # ntp_servers = ["ntp.aliyun.com"] 6 | 7 | # # response time out seconds 8 | # timeout = 5 -------------------------------------------------------------------------------- /integrations/Linux/collect/processes/processes.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # # force use ps command to gather 5 | # force_ps = false 6 | 7 | # # force use /proc to gather 8 | # force_proc = false -------------------------------------------------------------------------------- /integrations/Linux/icon/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Linux/icon/linux.png -------------------------------------------------------------------------------- /integrations/Linux/markdown/README.md: -------------------------------------------------------------------------------- 1 | # Linux 2 | 3 | Linux 类别下,包含多个内置插件,比如 cpu、mem、net、netstat、kernel_vmstat 等,这些插件大都是默认是开启的,无需额外配置,可能有额外配置需求的插件如下。 4 | 5 | ## cpu 6 | 7 | 统计 CPU 使用率,默认只采集整机的情况,不采集每个 CPU Core 的情况,如果想采集每个 CPU Core 的情况,可以配置如下。 8 | 9 | ```ini 10 | collect_per_cpu = true 11 | ``` 12 | 13 | ## netstat 14 | 15 | 统计网络连接数,默认配置如下,可根据实际情况调整。 16 | 17 | ```ini 18 | # 默认开启了 smmary 统计,类似 ss -s 命令的输出 19 | disable_summary_stats = false 20 | # 默认关闭了所有连接的详细统计,在连接数较多的机器上统计此数据会影响性能 21 | disable_connection_stats = true 22 | # 读取 /proc/net/netstat 的内容,默认关闭了,可以开启,这部分不影响性能 23 | tcp_ext = false 24 | ip_ext = false 25 | ``` 26 | 27 | ## disk 28 | 29 | 统计磁盘使用率,默认配置如下,可根据实际情况调整。 30 | 31 | ```ini 32 | # 严格指定要采集的挂载点,如果指定了,就只采集指定的挂载点 33 | # mount_points = ["/"] 34 | 35 | # 有些 fstype 没必要采集,可以忽略 36 | ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs", "nsfs", "CDFS", "fuse.juicefs"] 37 | 38 | # 有些挂载点没必要采集,可以忽略,这里可以配置前缀,符合前缀的挂载点都会被忽略 39 | ignore_mount_points = ["/boot", "/var/lib/kubelet/pods"] 40 | ``` 41 | 42 | ## kernel_vmstat 43 | 44 | 统计的信息来自 `/proc/vmstat`,只有高版本内核才支持,这个文件的内容较多,默认配置只采集了 oom_kill 次数,其他指标均未采集,如果你想打开其他采集开关,可以修改 white_list 部分的配置。下面是截取了一部分内容,供参考: 45 | 46 | ```toml 47 | [white_list] 48 | oom_kill = 1 49 | nr_free_pages = 0 50 | nr_alloc_batch = 0 51 | ... 52 | ``` 53 | 54 | ## arp_package 55 | 56 | 统计 ARP 包的数量,该插件依赖 cgo,如果需要该插件需要下载 `with-cgo` 的 categraf 发布包。 57 | 58 | 59 | ## ntp 60 | 61 | 监控机器时间偏移量,只需要给出 ntp 服务端地址,Categraf 就会周期性去请求,对比本机时间,得到偏移量,监控指标是 ntp_offset_ms 顾名思义,单位是毫秒,一般这个值不能超过 1000 -------------------------------------------------------------------------------- /integrations/Logstash/collect/logstash/logstash.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # Read metrics exposed by Logstash 5 | [[instances]] 6 | # # interval = global.interval * interval_times 7 | # interval_times = 1 8 | 9 | # append labels 10 | # labels = { instance="x" } 11 | 12 | ## The URL of the exposed Logstash API endpoint. 13 | # url = "http://127.0.0.1:9600" 14 | url = "" 15 | 16 | ## Use Logstash 5 single pipeline API, set to true when monitoring 17 | ## Logstash 5. 18 | # single_pipeline = false 19 | 20 | ## Enable optional collection components. Can contain 21 | ## "pipelines", "process", and "jvm". 22 | # collect = ["pipelines", "process", "jvm"] 23 | 24 | ## Timeout for HTTP requests. 25 | # timeout = "5s" 26 | 27 | ## Optional HTTP Basic Auth credentials. 28 | # username = "username" 29 | # password = "pa$$word" 30 | 31 | ## Optional HTTP headers. 32 | # [inputs.logstash.headers] 33 | # "X-Special-Header" = "Special-Value" 34 | 35 | ## Optional TLS Config 36 | # use_tls = false 37 | # tls_min_version = "1.2" 38 | # tls_ca = "/etc/categraf/ca.pem" 39 | # tls_cert = "/etc/categraf/cert.pem" 40 | # tls_key = "/etc/categraf/key.pem" 41 | ## Use TLS but skip chain & host verification 42 | # insecure_skip_verify = true -------------------------------------------------------------------------------- /integrations/Logstash/icon/logstash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Logstash/icon/logstash.png -------------------------------------------------------------------------------- /integrations/Logstash/markdown/README.md: -------------------------------------------------------------------------------- 1 | # logstash 2 | 3 | logstash 监控采集插件,由telegraf改造而来。 4 | 5 | ## Configuration 6 | 7 | 请参考配置[示例](https://github.com/flashcatcloud/categraf/blob/main/conf/input.logstash/logstash.toml) 8 | 9 | ## 监控大盘和告警规则 10 | 11 | 同级目录下的 logstash-dash 是示例的监控面板, 可以直接导入夜莺使用。 -------------------------------------------------------------------------------- /integrations/MinIO/icon/minio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MinIO/icon/minio.png -------------------------------------------------------------------------------- /integrations/MinIO/markdown/README.md: -------------------------------------------------------------------------------- 1 | # MinIO 2 | 3 | 参考 [使用 Prometheus 采集 MinIO 指标](https://min.io/docs/minio/linux/operations/monitoring/collect-minio-metrics-using-prometheus.html?ref=docs-redirect#minio-metrics-collect-using-prometheus) 4 | 5 | 开启 MinIO Prometheus 访问; 6 | 7 | ```bash 8 | # 启动 MinIO 服务的时候加入下面的变量: 9 | MINIO_PROMETHEUS_AUTH_TYPE=public 10 | ``` 11 | 12 | ## 采集配置 13 | 14 | categraf 的 `conf/input.prometheus/prometheus.toml` 15 | 16 | ```toml 17 | [[instances]] 18 | urls = [ 19 | "http://192.168.1.188:9000/minio/v2/metrics/cluster" 20 | ] 21 | labels = {job="minio-cluster"} 22 | ``` 23 | 24 | ## Dashboard 25 | 26 | 夜莺内置了 MinIO 的仪表盘,克隆到自己的业务组下即可使用。 27 | 28 | ![20230801170735](https://download.flashcat.cloud/ulric/20230801170735.png) 29 | 30 | ## Alerts 31 | 32 | 夜莺内置了 MinIO 的告警规则,克隆到自己的业务组下即可使用。 33 | 34 | ![20230801170725](https://download.flashcat.cloud/ulric/20230801170725.png) -------------------------------------------------------------------------------- /integrations/MinIO/markdown/alerts-minio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MinIO/markdown/alerts-minio.png -------------------------------------------------------------------------------- /integrations/MinIO/markdown/alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MinIO/markdown/alerts.png -------------------------------------------------------------------------------- /integrations/MinIO/markdown/dash-minio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MinIO/markdown/dash-minio.png -------------------------------------------------------------------------------- /integrations/MinIO/markdown/minio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MinIO/markdown/minio.png -------------------------------------------------------------------------------- /integrations/MongoDB/icon/mongodb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MongoDB/icon/mongodb.png -------------------------------------------------------------------------------- /integrations/Mtail/collect/mtail/mtail.toml: -------------------------------------------------------------------------------- 1 | [[instances]] 2 | # progs = "/path/to/prog1" # prog dir1 3 | # logs = ["/path/to/a.log", "path/to/b.log"] 4 | # override_timezone = "Asia/Shanghai" 5 | # emit_metric_timestamp = "true" #string type 6 | 7 | # [[instances]] 8 | # progs = "/path/to/prog2" # prog dir2 9 | # logs = ["/path/to/logdir/"] 10 | # override_timezone = "Asia/Shanghai" 11 | # emit_metric_timestamp = "true" # string type 12 | -------------------------------------------------------------------------------- /integrations/Mtail/icon/mtail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Mtail/icon/mtail.png -------------------------------------------------------------------------------- /integrations/Mtail/markdown/timestamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Mtail/markdown/timestamp.png -------------------------------------------------------------------------------- /integrations/Mtail/markdown/timezone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Mtail/markdown/timezone.png -------------------------------------------------------------------------------- /integrations/MySQL/icon/mysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/MySQL/icon/mysql.png -------------------------------------------------------------------------------- /integrations/N9E/icon/nightingale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/N9E/icon/nightingale.png -------------------------------------------------------------------------------- /integrations/N9E/markdown/README.md: -------------------------------------------------------------------------------- 1 | # N9E 2 | 3 | 夜莺V5版本分两个组件,n9e-webapi 和 n9e-server,都通过 `/metrics` 接口暴露了 Prometheus 协议的监控数据。夜莺V6版本默认只有一个组件,就是 n9e,也通过 `/metrics` 接口暴露了 Prometheus 协议的监控数据。如果使用边缘机房部署方案,会用到 n9e-edge,n9e-edge 也通过 `/metrics` 接口暴露了 Prometheus 协议的监控数据。 4 | 5 | 所以,通过 categraf 的 prometheus 插件即可采集夜莺的监控数据。 6 | 7 | ## 采集配置 8 | 9 | categraf 的 `conf/input.prometheus/prometheus.toml` 10 | 11 | ```toml 12 | [[instances]] 13 | urls = [ 14 | "http://IP:17000/metrics" 15 | ] 16 | labels = {job="n9e"} 17 | ``` 18 | 19 | ## Dashboard 20 | 21 | 夜莺内置了两个 N9E 仪表盘,n9e_server 是给 V5 版本用的,n9e_v6 是给 V6 版本用的。 22 | 23 | -------------------------------------------------------------------------------- /integrations/NFSClient/collect/nfsclient/nfsclient.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | ## Read more low-level metrics (optional, defaults to false) 5 | fullstat = false 6 | 7 | ## List of mounts to explictly include or exclude (optional) 8 | ## The pattern (Go regexp) is matched against the mount point (not the 9 | ## device being mounted). If include_mounts is set, all mounts are ignored 10 | ## unless present in the list. If a mount is listed in both include_mounts 11 | ## and exclude_mounts, it is excluded. Go regexp patterns can be used. 12 | 13 | # include_mounts = [] 14 | # exclude_mounts = [] 15 | 16 | ## List of operations to include or exclude from collecting. This applies 17 | ## only when fullstat=true. Symantics are similar to {include,exclude}_mounts: 18 | ## the default is to collect everything; when include_operations is set, only 19 | ## those OPs are collected; when exclude_operations is set, all are collected 20 | ## except those listed. If include and exclude are set, the OP is excluded. 21 | ## See /proc/self/mountstats for a list of valid operations; note that 22 | ## NFSv3 and NFSv4 have different lists. While it is not possible to 23 | ## have different include/exclude lists for NFSv3/4, unused elements 24 | ## in the list should be okay. It is possible to have different lists 25 | ## for different mountpoints: use mulitple [[input.nfsclient]] stanzas, 26 | ## with their own lists. See "include_mounts" above, and be careful of 27 | ## duplicate metrics. 28 | 29 | # include_operations = ['READ','WRITE','ACCESS','GETATTR','READDIR','LOOKUP'] 30 | # exclude_operations = [] 31 | -------------------------------------------------------------------------------- /integrations/NFSClient/icon/nfsclient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/NFSClient/icon/nfsclient.png -------------------------------------------------------------------------------- /integrations/NFSClient/markdown/README.md: -------------------------------------------------------------------------------- 1 | # NFS Client 2 | 3 | forked from telegraf/inputs.nfsclient 4 | 5 | ## 停用该插件 6 | 7 | - 方法一:把 `input.nfsclient` 目录改个别的名字,不用 `input.` 打头 8 | - 方法二:nfsclient.toml 中的配置留空 -------------------------------------------------------------------------------- /integrations/NSQ/collect/nsq/nsq.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # [[instances]] 5 | ## The Nsq API URI used to collect statistical information. 6 | # targets = ["http://localhost:4151"] 7 | 8 | # headers={Authorization="", X-Forwarded-For="", Host=""} 9 | 10 | # timeout="5s" 11 | 12 | # # basic auth 13 | # username="" 14 | # password="" 15 | 16 | ## append some labels for series 17 | # labels = { product="nsq" } 18 | 19 | ## interval = global.interval * interval_times 20 | # interval_times = 1 21 | -------------------------------------------------------------------------------- /integrations/NSQ/icon/nsq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/NSQ/icon/nsq.png -------------------------------------------------------------------------------- /integrations/NVIDIA/collect/nvidia_smi/nvidia_smi.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # exec local command 5 | # e.g. nvidia_smi_command = "nvidia-smi" 6 | nvidia_smi_command = "" 7 | 8 | # exec remote command 9 | # nvidia_smi_command = "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null SSH_USER@SSH_HOST nvidia-smi" 10 | 11 | # Comma-separated list of the query fields. 12 | # You can find out possible fields by running `nvidia-smi --help-query-gpus`. 13 | # The value `AUTO` will automatically detect the fields to query. 14 | query_field_names = "AUTO" -------------------------------------------------------------------------------- /integrations/NVIDIA/icon/nvidia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/NVIDIA/icon/nvidia.png -------------------------------------------------------------------------------- /integrations/NVIDIA/markdown/README.md: -------------------------------------------------------------------------------- 1 | # nvidia_smi 2 | 3 | 该采集插件的原理,就是读取 nvidia-smi 命令的内容输出,转换为Prometheus格式的监控数据上报给Nightingale夜莺。 4 | 5 | 是对 [nvidia_gpu_exporter](https://github.com/utkuozdemir/nvidia_gpu_exporter) 代码的集成。 6 | 7 | ## Configuration 8 | 9 | 配置文件在 `conf/input.nvidia_smi/nvidia_smi.toml` 10 | 11 | ```toml 12 | # # collect interval 13 | # interval = 15 14 | 15 | # 下面这个配置是最重要的配置,如果要采集 nvidia-smi 的信息,就打开下面的配置, 16 | # 给出 nvidia-smi 命令的路径,最好是给绝对路径 17 | # 相当于让 Categraf 执行本机的 nvidia-smi 命令,获取本机 GPU 的状态信息 18 | # exec local command 19 | # nvidia_smi_command = "nvidia-smi" 20 | 21 | # 如果想远程方式采集远端机器的 GPU 状态信息,可以使用 ssh 命令,登录远端机器 22 | # 在远端机器执行 nvidia-smi 的命令输出,通常 Categraf 是部署在每个物理机上的 23 | # 所以,ssh 这种方式,理论上用不到 24 | # exec remote command 25 | # nvidia_smi_command = "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null SSH_USER@SSH_HOST nvidia-smi" 26 | 27 | # Comma-separated list of the query fields. 28 | # You can find out possible fields by running `nvidia-smi --help-query-gpus`. 29 | # The value `AUTO` will automatically detect the fields to query. 30 | query_field_names = "AUTO" 31 | ``` 32 | 33 | ## TODO 34 | 35 | GPU 卡已经关注哪些监控指标,缺少监控大盘JSON和告警规则JSON,欢迎大家 PR 36 | -------------------------------------------------------------------------------- /integrations/Net_Response/alerts/net_response_by_categraf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "group_id": 0, 5 | "cate": "", 6 | "datasource_ids": null, 7 | "cluster": "", 8 | "name": "Network address probe failed", 9 | "note": "", 10 | "prod": "", 11 | "algorithm": "", 12 | "algo_params": null, 13 | "delay": 0, 14 | "severity": 2, 15 | "severities": null, 16 | "disabled": 0, 17 | "prom_for_duration": 60, 18 | "prom_ql": "net_response_result_code != 0", 19 | "rule_config": null, 20 | "prom_eval_interval": 15, 21 | "enable_stime": "00:00", 22 | "enable_stimes": null, 23 | "enable_etime": "23:59", 24 | "enable_etimes": null, 25 | "enable_days_of_week": [ 26 | "1", 27 | "2", 28 | "3", 29 | "4", 30 | "5", 31 | "6", 32 | "0" 33 | ], 34 | "enable_days_of_weeks": null, 35 | "enable_in_bg": 0, 36 | "notify_recovered": 1, 37 | "notify_channels": [], 38 | "notify_groups_obj": null, 39 | "notify_groups": null, 40 | "notify_repeat_step": 60, 41 | "notify_max_number": 0, 42 | "recover_duration": 0, 43 | "callbacks": [], 44 | "runbook_url": "", 45 | "append_tags": [], 46 | "annotations": null, 47 | "extra_config": null, 48 | "create_at": 0, 49 | "create_by": "", 50 | "update_at": 0, 51 | "update_by": "", 52 | "uuid": 1717556328182186000 53 | } 54 | ] -------------------------------------------------------------------------------- /integrations/Net_Response/collect/net_response/net_response.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [mappings] 5 | # "127.0.0.1:22"= {region="local",ssh="test"} 6 | # "127.0.0.1:22"= {region="local",ssh="redis"} 7 | 8 | [[instances]] 9 | targets = [ 10 | # "127.0.0.1:22", 11 | # "localhost:6379", 12 | # ":9090" 13 | ] 14 | 15 | # # append some labels for series 16 | # labels = { region="cloud", product="n9e" } 17 | 18 | # # interval = global.interval * interval_times 19 | # interval_times = 1 20 | 21 | ## Protocol, must be "tcp" or "udp" 22 | ## NOTE: because the "udp" protocol does not respond to requests, it requires 23 | ## a send/expect string pair (see below). 24 | # protocol = "tcp" 25 | 26 | ## Set timeout 27 | # timeout = "1s" 28 | 29 | ## Set read timeout (only used if expecting a response) 30 | # read_timeout = "1s" 31 | 32 | ## The following options are required for UDP checks. For TCP, they are 33 | ## optional. The plugin will send the given string to the server and then 34 | ## expect to receive the given 'expect' string back. 35 | ## string sent to the server 36 | # send = "ssh" 37 | ## expected string in answer 38 | # expect = "ssh" 39 | -------------------------------------------------------------------------------- /integrations/Net_Response/icon/net_response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Net_Response/icon/net_response.png -------------------------------------------------------------------------------- /integrations/Net_Response/metrics/categraf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "uuid": 1717556328185013000, 5 | "collector": "Categraf", 6 | "typ": "Net_Response", 7 | "name": "NET 探测结果状态码", 8 | "unit": "none", 9 | "note": "0 值表示正常,大于 0 就是异常,各个值的含义如下:\n\n- 0: Success\n- 1: Timeout\n- 2: ConnectionFailed\n- 3: ReadFailed\n- 4: StringMismatch", 10 | "lang": "zh_CN", 11 | "expression": "net_response_result_code", 12 | "created_at": 0, 13 | "created_by": "", 14 | "updated_at": 0, 15 | "updated_by": "" 16 | }, 17 | { 18 | "id": 0, 19 | "uuid": 1717556328186975000, 20 | "collector": "Categraf", 21 | "typ": "Net_Response", 22 | "name": "NET 探测耗时", 23 | "unit": "seconds", 24 | "note": "", 25 | "lang": "zh_CN", 26 | "expression": "net_response_response_time", 27 | "created_at": 0, 28 | "created_by": "", 29 | "updated_at": 0, 30 | "updated_by": "" 31 | } 32 | ] -------------------------------------------------------------------------------- /integrations/Netstat_Filter/collect/netstat_filter/netstat_filter.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | [[instances]] 4 | # laddr_ip = "" 5 | # laddr_port = 0 6 | # raddr_ip = "" 7 | # raddr_port = 0 8 | -------------------------------------------------------------------------------- /integrations/Netstat_Filter/icon/netstat_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Netstat_Filter/icon/netstat_filter.png -------------------------------------------------------------------------------- /integrations/Netstat_Filter/markdown/README.md: -------------------------------------------------------------------------------- 1 | # netstat_filter 2 | 3 | 该插件采集网络连接情况,并根据用户条件进行过滤统计,以达到监控用户关心链接情况 4 | ## 指标列表 5 | tcp_established 6 | tcp_syn_sent 7 | tcp_syn_recv 8 | tcp_fin_wait1 9 | tcp_fin_wait2 10 | tcp_time_wait 11 | tcp_close 12 | tcp_close_wait 13 | tcp_last_ack 14 | tcp_listen 15 | tcp_closing 16 | tcp_none 17 | tcp_send_queue 18 | tcp_recv_queue 19 | 20 | ## 功能说明 21 | 对源IP、源端口、目标IP和目标端口过滤后进行网卡recv-Q、send-Q进行采集,该指标可以很好反应出指定连接的质量,例如rtt时间过长,导致收到服务端ack确认很慢就会使send-Q长期大于0,可以及时通过监控发现,从而提前优化网络或程序 22 | 23 | 当过滤结果为多个连接时会将send和recv值进行加和 24 | 例如: 25 | 配置文件``raddr_port = 11883`` 26 | 当本地和不同IP的11883都有连接建立的情况下,会将多条连接的结果进行加和。或在并发多连接的情况下,会合并加合,总之过滤的越粗略被加合数就会越多。 27 | 28 | 多条规则请复制``[[instances]]``进行配置 29 | 30 | ## 注意事项 31 | netstat_filter_tcp_send_queue和netstat_filter_tcp_recv_queue指标目前只支持linux。windows用户默认为0。 32 | -------------------------------------------------------------------------------- /integrations/Nginx/collect/nginx/nginx.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | ## An array of Nginx stub_status URI to gather stats. 6 | urls = [ 7 | # "http://192.168.0.216:8000/nginx_status", 8 | # "https://www.baidu.com/ngx_status" 9 | ] 10 | 11 | ## append some labels for series 12 | # labels = { region="cloud", product="n9e" } 13 | 14 | ## interval = global.interval * interval_times 15 | # interval_times = 1 16 | 17 | ## Set response_timeout (default 5 seconds) 18 | response_timeout = "5s" 19 | 20 | ## Whether to follow redirects from the server (defaults to false) 21 | # follow_redirects = false 22 | 23 | ## Optional HTTP Basic Auth Credentials 24 | #username = "admin" 25 | #password = "admin" 26 | 27 | ## Optional headers 28 | # headers = ["X-From", "categraf", "X-Xyz", "abc"] 29 | 30 | ## Optional TLS Config 31 | # use_tls = false 32 | # tls_ca = "/etc/categraf/ca.pem" 33 | # tls_cert = "/etc/categraf/cert.pem" 34 | # tls_key = "/etc/categraf/key.pem" 35 | ## Use TLS but skip chain & host verification 36 | # insecure_skip_verify = false -------------------------------------------------------------------------------- /integrations/Nginx/collect/nginx_upstream_check/nginx_upstream_check.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | targets = [ 6 | # "http://127.0.0.1/status?format=json", 7 | # "http://10.2.3.56/status?format=json" 8 | ] 9 | 10 | # # append some labels for series 11 | # labels = { region="cloud", product="n9e" } 12 | 13 | # # interval = global.interval * interval_times 14 | # interval_times = 1 15 | 16 | ## Set http_proxy (categraf uses the system wide proxy settings if it's is not set) 17 | # http_proxy = "http://localhost:8888" 18 | 19 | ## Interface to use when dialing an address 20 | # interface = "eth0" 21 | 22 | ## HTTP Request Method 23 | # method = "GET" 24 | 25 | ## Set timeout (default 5 seconds) 26 | # timeout = "5s" 27 | 28 | ## Whether to follow redirects from the server (defaults to false) 29 | # follow_redirects = false 30 | 31 | ## Optional HTTP Basic Auth Credentials 32 | # username = "username" 33 | # password = "pa$$word" 34 | 35 | ## Optional headers 36 | # headers = ["X-From", "categraf", "X-Xyz", "abc"] 37 | 38 | ## Optional TLS Config 39 | # use_tls = false 40 | # tls_ca = "/etc/categraf/ca.pem" 41 | # tls_cert = "/etc/categraf/cert.pem" 42 | # tls_key = "/etc/categraf/key.pem" 43 | ## Use TLS but skip chain & host verification 44 | # insecure_skip_verify = false 45 | -------------------------------------------------------------------------------- /integrations/Nginx/icon/nginx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Nginx/icon/nginx.png -------------------------------------------------------------------------------- /integrations/Oracle/icon/oracle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Oracle/icon/oracle.png -------------------------------------------------------------------------------- /integrations/Oracle/markdown/README.md: -------------------------------------------------------------------------------- 1 | # Oracle plugin 2 | 3 | Oracle 插件,用于监控 Oracle 数据库。默认无法跑在 Windows 上。如果你的 Oracle 部署在 Windows 上,也没问题,使用部署在 Linux 上的 Categraf 远程监控 Windows 上的 Oracle,也行得通。 4 | 5 | Oracle 插件的核心监控原理,就是执行下面 [这些 SQL 语句](https://github.com/flashcatcloud/categraf/blob/main/conf/input.oracle/metric.toml),然后解析出结果,上报到监控服务端。 6 | 7 | 以其中一个为例: 8 | 9 | ```toml 10 | [[metrics]] 11 | mesurement = "activity" 12 | metric_fields = [ "value" ] 13 | field_to_append = "name" 14 | timeout = "3s" 15 | request = ''' 16 | SELECT name, value FROM v$sysstat WHERE name IN ('parse count (total)', 'execute count', 'user commits', 'user rollbacks') 17 | ''' 18 | ``` 19 | 20 | - mesurement:指标类别 21 | - label_fields:作为 label 的字段 22 | - metric_fields:作为 metric 的字段,因为是作为 metric 的字段,所以这个字段的值必须是数字 23 | - field_to_append:表示这个字段附加到 metric_name 后面,作为 metric_name 的一部分 24 | - timeout:超时时间 25 | - request:具体查询的 SQL 语句 26 | 27 | 如果你想监控的指标,默认没有采集,只需要增加自定义的 `[[metrics]]` 配置即可。 28 | -------------------------------------------------------------------------------- /integrations/PHP/collect/phpfpm/phpfpm.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | ## An array of Nginx stub_status URI to gather stats. 6 | urls = [ 7 | ## HTTP: the URL must start with http:// or https://, ie: 8 | # "http://localhost/status", 9 | # "https://www.baidu.com/phpfpm-status", 10 | ## fcgi: the URL must start with fcgi:// or cgi://, and port must be present, ie: 11 | # "fcgi://127.0.0.1:9001", 12 | # "cgi://192.168.0.1:9000/status", 13 | ## Unix socket: path to fpm socket, ie: 14 | # "/run/php/php7.2-fpm.sock", 15 | ## or using a custom fpm status path: 16 | # "/var/run/php5-fpm.sock:/fpm-custom-status-path", 17 | ## glob patterns are also supported: 18 | # "/var/run/php*.sock" 19 | ] 20 | 21 | ## append some labels for series 22 | # labels = { region="cloud", product="n9e" } 23 | 24 | ## interval = global.interval * interval_times 25 | # interval_times = 1 26 | 27 | ## Set response_timeout (default 5 seconds),HTTP urls only 28 | response_timeout = "5s" 29 | 30 | ## Whether to follow redirects from the server (defaults to false),HTTP urls only 31 | # follow_redirects = false 32 | 33 | ## Optional HTTP Basic Auth Credentials,HTTP urls only 34 | #username = "admin" 35 | #password = "admin" 36 | 37 | ## Optional headers,HTTP urls only 38 | # headers = ["X-From", "categraf", "X-Xyz", "abc"] 39 | 40 | ## Optional TLS Config,only http 41 | # use_tls = false 42 | # tls_ca = "/etc/categraf/ca.pem" 43 | # tls_cert = "/etc/categraf/cert.pem" 44 | # tls_key = "/etc/categraf/key.pem" 45 | ## Use TLS but skip chain & host verification 46 | # insecure_skip_verify = false -------------------------------------------------------------------------------- /integrations/PHP/icon/phpfpm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/PHP/icon/phpfpm.png -------------------------------------------------------------------------------- /integrations/PHP/markdown/README.md: -------------------------------------------------------------------------------- 1 | # PHP-FPM 2 | 3 | *PHP-FPM* (PHP FastCGI Process Manager) 监控采集插件,由telegraf的phpfpm改造而来。 4 | 5 | 该插件需要更改phpfpm的配置文件,开启 *pm.status_path*配置项 6 | ``` 7 | pm.status_path = /status 8 | ``` 9 | 10 | 11 | ## Configuration 12 | 13 | 请参考配置[示例](https://github.com/flashcatcloud/categraf/blob/main/conf/input.phpfpm/phpfpm.toml)文件 14 | 15 | ### 注意事项: 16 | 1. 如下配置 仅生效于HTTP的url 17 | - response_timeout 18 | - username & password 19 | - headers 20 | - TLS config 21 | 2. 如果使用 Unix socket,需要保证 categraf 和 socket path 在同一个主机上,且 categraf 运行用户拥有读取该 path 的权限。 22 | ## 监控大盘和告警规则 23 | 24 | 待更新... -------------------------------------------------------------------------------- /integrations/Ping/collect/ping/ping.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # send ping packets to 6 | targets = [ 7 | # "www.baidu.com", 8 | # "127.0.0.1", 9 | # "10.4.5.6", 10 | # "10.4.5.7" 11 | ] 12 | 13 | # # append some labels for series 14 | # labels = { region="cloud", product="n9e" } 15 | 16 | # # interval = global.interval * interval_times 17 | # interval_times = 1 18 | 19 | ## Number of ping packets to send per interval. Corresponds to the "-c" 20 | ## option of the ping command. 21 | # count = 1 22 | 23 | ## Time to wait between sending ping packets in seconds. Operates like the 24 | ## "-i" option of the ping command. 25 | # ping_interval = 1.0 26 | 27 | ## If set, the time to wait for a ping response in seconds. Operates like 28 | ## the "-W" option of the ping command. 29 | # timeout = 3.0 30 | 31 | ## Interface or source address to send ping from. Operates like the -I or -S 32 | ## option of the ping command. 33 | # interface = "" 34 | 35 | ## Use only IPv6 addresses when resolving a hostname. 36 | # ipv6 = false 37 | 38 | ## Number of data bytes to be sent. Corresponds to the "-s" 39 | ## option of the ping command. 40 | # size = 56 41 | 42 | # max concurrency coroutine 43 | # concurrency = 50 44 | -------------------------------------------------------------------------------- /integrations/Ping/icon/ping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Ping/icon/ping.png -------------------------------------------------------------------------------- /integrations/Ping/markdown/README.md: -------------------------------------------------------------------------------- 1 | # ping 2 | 3 | ping 监控插件,探测远端目标地址能否 ping 通,如果机器没有禁 ping,这就是一个很好用的探测机器存活的手段 4 | 5 | ## Configuration 6 | 7 | categraf 的 `conf/input.ping/ping.toml`。 8 | 9 | 要探测的机器配置到 targets 中,targets 是个数组,可以配置多个,当然也可以拆成多个 `[[instances]]` 配置段,比如: 10 | 11 | ``` 12 | [[instances]] 13 | targets = [ "10.4.5.6" ] 14 | labels = { region="cloud", product="n9e" } 15 | 16 | [[instances]] 17 | targets = [ "10.4.5.7" ] 18 | labels = { region="cloud", product="zbx" } 19 | ``` 20 | 21 | 上例中是 ping 两个地址,为了信息更丰富,附加了 region 和 product 标签 22 | 23 | ## File Limit 24 | 25 | ```sh 26 | systemctl edit categraf 27 | ``` 28 | 29 | Increase the number of open files: 30 | 31 | ```ini 32 | [Service] 33 | LimitNOFILE=8192 34 | ``` 35 | 36 | Restart Categraf: 37 | 38 | ```sh 39 | systemctl restart categraf 40 | ``` 41 | 42 | ### Linux Permissions 43 | 44 | On most systems, ping requires `CAP_NET_RAW` capabilities or for Categraf to be run as root. 45 | 46 | With systemd: 47 | 48 | ```sh 49 | systemctl edit categraf 50 | ``` 51 | 52 | ```ini 53 | [Service] 54 | CapabilityBoundingSet=CAP_NET_RAW 55 | AmbientCapabilities=CAP_NET_RAW 56 | ``` 57 | 58 | ```sh 59 | systemctl restart categraf 60 | ``` 61 | 62 | Without systemd: 63 | 64 | ```sh 65 | setcap cap_net_raw=eip /usr/bin/categraf 66 | ``` 67 | 68 | Reference [`man 7 capabilities`][man 7 capabilities] for more information about 69 | setting capabilities. 70 | 71 | [man 7 capabilities]: http://man7.org/linux/man-pages/man7/capabilities.7.html 72 | 73 | ### Other OS Permissions 74 | 75 | When using `method = "native"`, you will need permissions similar to the executable ping program for your OS. 76 | 77 | ## 监控大盘和告警规则 78 | 79 | 夜莺内置了告警规则和监控大盘,克隆到自己的业务组下即可使用。 80 | -------------------------------------------------------------------------------- /integrations/PostgreSQL/icon/postgresql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/PostgreSQL/icon/postgresql.png -------------------------------------------------------------------------------- /integrations/PostgreSQL/markdown/alerts-pg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/PostgreSQL/markdown/alerts-pg.png -------------------------------------------------------------------------------- /integrations/PostgreSQL/markdown/alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/PostgreSQL/markdown/alerts.png -------------------------------------------------------------------------------- /integrations/PostgreSQL/markdown/dash-pg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/PostgreSQL/markdown/dash-pg.png -------------------------------------------------------------------------------- /integrations/PostgreSQL/markdown/postgresql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/PostgreSQL/markdown/postgresql.png -------------------------------------------------------------------------------- /integrations/Procstat/collect/procstat/procstat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # [[instances]] 5 | # # executable name (ie, pgrep ) 6 | # search_exec_substring = "nginx" 7 | 8 | # # pattern as argument for pgrep (ie, pgrep -f ) 9 | # search_cmdline_substring = "n9e server" 10 | 11 | # # windows service name 12 | # search_win_service = "" 13 | 14 | # # search process with specific user, option with exec_substring or cmdline_substring 15 | # search_user = "" 16 | 17 | # # append some labels for series 18 | # labels = { region="cloud", product="n9e" } 19 | 20 | # # interval = global.interval * interval_times 21 | # interval_times = 1 22 | 23 | # # mode to use when calculating CPU usage. can be one of 'solaris' or 'irix' 24 | # mode = "irix" 25 | 26 | # sum of threads/fd/io/cpu/mem, min of uptime/limit 27 | gather_total = true 28 | 29 | # will append pid as tag 30 | gather_per_pid = false 31 | 32 | # gather jvm metrics only when jstat is ready 33 | # gather_more_metrics = [ 34 | # "threads", 35 | # "fd", 36 | # "io", 37 | # "uptime", 38 | # "cpu", 39 | # "mem", 40 | # "limit", 41 | # "jvm" 42 | # ] 43 | -------------------------------------------------------------------------------- /integrations/Procstat/icon/process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Procstat/icon/process.png -------------------------------------------------------------------------------- /integrations/Prometheus/collect/prometheus/prometheus.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | urls = [ 6 | # "http://localhost:19000/metrics" 7 | ] 8 | 9 | url_label_key = "instance" 10 | url_label_value = "{{.Host}}" 11 | 12 | ## Scrape Services available in Consul Catalog 13 | # [instances.consul] 14 | # enabled = false 15 | # agent = "http://localhost:8500" 16 | # query_interval = "5m" 17 | 18 | # [[instances.consul.query]] 19 | # name = "a service name" 20 | # tag = "a service tag" 21 | # url = 'http://{{if ne .ServiceAddress ""}}{{.ServiceAddress}}{{else}}{{.Address}}{{end}}:{{.ServicePort}}/{{with .ServiceMeta.metrics_path}}{{.}}{{else}}metrics{{end}}' 22 | # [instances.consul.query.tags] 23 | # host = "{{.Node}}" 24 | 25 | # bearer_token_string = "" 26 | 27 | # e.g. /run/secrets/kubernetes.io/serviceaccount/token 28 | # bearer_token_file = "" 29 | 30 | # # basic auth 31 | # username = "" 32 | # password = "" 33 | 34 | # headers = ["X-From", "categraf"] 35 | 36 | # # interval = global.interval * interval_times 37 | # interval_times = 1 38 | 39 | # labels = {} 40 | 41 | # support glob 42 | # ignore_metrics = [ "go_*" ] 43 | 44 | # support glob 45 | # ignore_label_keys = [] 46 | 47 | # timeout for every url 48 | # timeout = "3s" 49 | 50 | ## Optional TLS Config 51 | # use_tls = false 52 | # tls_min_version = "1.2" 53 | # tls_ca = "/etc/categraf/ca.pem" 54 | # tls_cert = "/etc/categraf/cert.pem" 55 | # tls_key = "/etc/categraf/key.pem" 56 | ## Use TLS but skip chain & host verification 57 | # insecure_skip_verify = true 58 | -------------------------------------------------------------------------------- /integrations/Prometheus/icon/prometheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Prometheus/icon/prometheus.png -------------------------------------------------------------------------------- /integrations/Prometheus/markdown/README.md: -------------------------------------------------------------------------------- 1 | # prometheus 2 | 3 | prometheus 插件的作用,就是抓取 `/metrics` 接口的数据,上报给服务端。通过,各类 exporter 会暴露 `/metrics` 接口数据,越来越多的开源组件也会内置 prometheus SDK,吐出 prometheus 格式的监控数据,比如 rabbitmq 插件,其 README 中就有介绍。 4 | 5 | 这个插件 fork 自 telegraf/prometheus,做了一些删减改造,仍然支持通过 consul 做服务发现,管理所有的目标地址,删掉了 Kubernetes 部分,Kubernetes 部分准备放到其他插件里实现。 6 | 7 | 增加了两个配置:url_label_key 和 url_label_value。为了标识监控数据是从哪个 scrape url 拉取的,会为监控数据附一个标签来标识这个 url,默认的标签 KEY 是用 instance,当然,也可以改成别的,不过不建议。url_label_value 是标签值,支持 go template 语法,如果为空,就是整个 url 的内容,也可以通过模板变量只取一部分,比如 `http://localhost:9104/metrics`,只想取 IP 和端口部分,就可以写成: 8 | 9 | ```ini 10 | url_label_value = "{{.Host}}" 11 | ``` 12 | 13 | 如果 HTTP scheme 部分和 `/metrics` Path 部分都想取,可以这么写: 14 | 15 | ```ini 16 | url_label_value = "{{.Scheme}}://{{.Host}}{{.Path}}" 17 | ``` 18 | 19 | 相关变量是用这个方法生成的,供大家参考: 20 | 21 | ```go 22 | func (ul *UrlLabel) GenerateLabel(u *url.URL) (string, string, error) { 23 | if ul.LabelValue == "" { 24 | return ul.LabelKey, u.String(), nil 25 | } 26 | 27 | dict := map[string]string{ 28 | "Scheme": u.Scheme, 29 | "Host": u.Host, 30 | "Hostname": u.Hostname(), 31 | "Port": u.Port(), 32 | "Path": u.Path, 33 | "Query": u.RawQuery, 34 | "Fragment": u.Fragment, 35 | } 36 | 37 | var buffer bytes.Buffer 38 | err := ul.LabelValueTpl.Execute(&buffer, dict) 39 | if err != nil { 40 | return "", "", err 41 | } 42 | 43 | return ul.LabelKey, buffer.String(), nil 44 | } 45 | ``` -------------------------------------------------------------------------------- /integrations/RabbitMQ/icon/rabbitmq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/RabbitMQ/icon/rabbitmq.png -------------------------------------------------------------------------------- /integrations/RabbitMQ/markdown/README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ 2 | 3 | 高版本(3.8以上版本)的 RabbitMQ,已经内置支持了暴露 Prometheus 协议的监控数据。所以,直接使用 categraf 的 prometheus 插件即可采集。开启 RabbitMQ Prometheus 访问: 4 | 5 | ```bash 6 | rabbitmq-plugins enable rabbitmq_prometheus 7 | ``` 8 | 9 | 启用成功的话,rabbitmq 默认会在 15692 端口起监听,访问 `http://localhost:15692/metrics` 即可看到符合 prometheus 协议的监控数据。 10 | 11 | 如果低于 3.8 的版本,还是需要使用 categraf 的 rabbitmq 插件来采集监控数据。 12 | 13 | ## 告警规则 14 | 15 | 夜莺内置了 RabbitMQ 的告警规则,克隆到自己的业务组下即可使用。 16 | 17 | ## 仪表盘 18 | 19 | 夜莺内置了 RabbitMQ 的仪表盘,克隆到自己的业务组下即可使用。`rabbitmq_v3.8_gt` 是大于等于 3.8 版本的仪表盘,`rabbitmq_v3.8_lt` 是小于 3.8 版本的仪表盘。 20 | 21 | ![20230802082542](https://download.flashcat.cloud/ulric/20230802082542.png) 22 | -------------------------------------------------------------------------------- /integrations/RabbitMQ/markdown/rabbitmq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/RabbitMQ/markdown/rabbitmq.png -------------------------------------------------------------------------------- /integrations/Redis/collect/redis/redis.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # address = "127.0.0.1:6379" 6 | # username = "" 7 | # password = "" 8 | # pool_size = 2 9 | 10 | # # Optional. Specify redis commands to retrieve values 11 | # commands = [ 12 | # {command = ["get", "sample-key1"], metric = "custom_metric_name1"}, 13 | # {command = ["get", "sample-key2"], metric = "custom_metric_name2"} 14 | # ] 15 | 16 | # # interval = global.interval * interval_times 17 | # interval_times = 1 18 | 19 | # important! use global unique string to specify instance 20 | # labels = { instance="n9e-10.2.3.4:6379" } 21 | 22 | ## Optional TLS Config 23 | # use_tls = false 24 | # tls_min_version = "1.2" 25 | # tls_ca = "/etc/categraf/ca.pem" 26 | # tls_cert = "/etc/categraf/cert.pem" 27 | # tls_key = "/etc/categraf/key.pem" 28 | ## Use TLS but skip chain & host verification 29 | # insecure_skip_verify = true 30 | -------------------------------------------------------------------------------- /integrations/Redis/collect/redis_sentinel/redis_sentinel.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # [protocol://][:password]@address[:port] 6 | # e.g. servers = ["tcp://localhost:26379"] 7 | servers = [] 8 | 9 | # # interval = global.interval * interval_times 10 | # interval_times = 1 11 | # add some dimension data by labels 12 | # labels = {} 13 | 14 | ## Optional TLS Config 15 | # use_tls = false 16 | # tls_min_version = "1.2" 17 | # tls_ca = "/etc/categraf/ca.pem" 18 | # tls_cert = "/etc/categraf/cert.pem" 19 | # tls_key = "/etc/categraf/key.pem" 20 | ## Use TLS but skip chain & host verification 21 | # insecure_skip_verify = true 22 | -------------------------------------------------------------------------------- /integrations/Redis/icon/redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Redis/icon/redis.png -------------------------------------------------------------------------------- /integrations/Redis/markdown/README.md: -------------------------------------------------------------------------------- 1 | # redis 2 | 3 | redis 的监控原理,就是连上 redis,执行 info 命令,解析结果,整理成监控数据上报。 4 | 5 | ## Configuration 6 | 7 | redis 插件的配置在 `conf/input.redis/redis.toml` 最简单的配置如下: 8 | 9 | ```toml 10 | [[instances]] 11 | address = "127.0.0.1:6379" 12 | username = "" 13 | password = "" 14 | labels = { instance="n9e-10.23.25.2:6379" } 15 | ``` 16 | 17 | 如果要监控多个 redis 实例,就增加 instances 即可: 18 | 19 | ```toml 20 | [[instances]] 21 | address = "10.23.25.2:6379" 22 | username = "" 23 | password = "" 24 | labels = { instance="n9e-10.23.25.2:6379" } 25 | 26 | [[instances]] 27 | address = "10.23.25.3:6379" 28 | username = "" 29 | password = "" 30 | labels = { instance="n9e-10.23.25.3:6379" } 31 | ``` 32 | 33 | 建议通过 labels 配置附加一个 instance 标签,便于后面复用监控大盘。 34 | 35 | ## 监控大盘和告警规则 36 | 37 | 夜莺内置了 redis 的告警规则和监控大盘,克隆到自己的业务组下即可使用。 38 | 39 | ## redis 集群如何监控 40 | 41 | 其实,redis 集群的监控,还是去监控每个 redis 实例。 42 | 43 | 如果一个 redis 集群有 3 个实例,对于业务应用来讲,发起一个请求,可能随机请求到某一个实例上去了,这个是没问题的,但是对于监控 client 而言,显然是希望到所有实例上获取数据的。 44 | 45 | 当然,如果多个 redis 实例组成了集群,我们希望有个标识来标识这个集群,这个时候,可以通过 labels 来实现,比如给每个实例增加一个 redis_clus 的标签,值为集群名字即可。 46 | 47 | 48 | # redis_sentinel 49 | forked from [telegraf/redis_sentinel](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/redis_sentinel) 50 | -------------------------------------------------------------------------------- /integrations/SMART/icon/smart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/SMART/icon/smart.png -------------------------------------------------------------------------------- /integrations/SNMP/icon/snmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/SNMP/icon/snmp.png -------------------------------------------------------------------------------- /integrations/SQLServer/icon/sqlserver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/SQLServer/icon/sqlserver.png -------------------------------------------------------------------------------- /integrations/SQLServer/markdown/README.md: -------------------------------------------------------------------------------- 1 | # sqlserver 2 | 3 | forked from telegraf/sqlserver. 这个插件的作用是获取sqlserver的监控指标,这里去掉了Azure相关部分监控,只保留了本地部署sqlserver情况。 4 | 5 | # 使用 6 | 按照下面方法创建监控账号,用于读取监控数据 7 | USE master; 8 | 9 | CREATE LOGIN [categraf] WITH PASSWORD = N'mystrongpassword'; 10 | 11 | GRANT VIEW SERVER STATE TO [categraf]; 12 | 13 | GRANT VIEW ANY DEFINITION TO [categraf]; 14 | Data Source=10.19.1.1;Initial Catalog=hc;User ID=sa;Password=mystrongpassword; -------------------------------------------------------------------------------- /integrations/SpringBoot/icon/springboot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/SpringBoot/icon/springboot.png -------------------------------------------------------------------------------- /integrations/SpringBoot/markdown/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot 2 | 3 | Java 生态的项目,如果要暴露 metrics 数据,一般可以选择 micrometer,不过 SpringBoot 项目可以直接使用 SpringBoot Actuator 暴露 metrics 数据,Actuator 底层也是使用 micrometer 来实现的,只是使用起来更加简单。 4 | 5 | ## 应用配置 6 | 7 | 在 application.properties 中加入如下配置: 8 | 9 | ```properties 10 | management.endpoint.metrics.enabled=true 11 | management.endpoints.web.exposure.include=* 12 | management.endpoint.prometheus.enabled=true 13 | management.metrics.export.prometheus.enabled=true 14 | ``` 15 | 16 | 完事启动项目,访问 `http://localhost:8080/actuator/prometheus` 即可看到符合 prometheus 协议的监控数据。 17 | 18 | ## 采集配置 19 | 20 | 既然暴露了 Prometheus 协议的监控数据,那通过 categraf prometheus 插件直接采集即可。配置文件是 `conf/input.prometheus/prometheus.toml`。配置样例如下: 21 | 22 | ```toml 23 | [[instances]] 24 | urls = [ 25 | "http://192.168.11.177:8080/actuator/prometheus" 26 | ] 27 | ``` 28 | 29 | ## 仪表盘 30 | 31 | 夜莺内置了一个 SpringBoot 仪表盘,由网友贡献,克隆到自己的业务组下即可使用,欢迎大家一起来提 PR 完善。 32 | 33 | ![actuator2.0](http://download.flashcat.cloud/uPic/actuator_2.0.png) 34 | -------------------------------------------------------------------------------- /integrations/SpringBoot/markdown/actuator.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/SpringBoot/markdown/actuator.jpeg -------------------------------------------------------------------------------- /integrations/SpringBoot/markdown/actuator_2.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/SpringBoot/markdown/actuator_2.0.png -------------------------------------------------------------------------------- /integrations/Switch_Legacy/icon/switch_legacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Switch_Legacy/icon/switch_legacy.png -------------------------------------------------------------------------------- /integrations/Switch_Legacy/markdown/README.md: -------------------------------------------------------------------------------- 1 | # switch_legacy 2 | 3 | 交换机监控插件,fork 自 [https://github.com/gaochao1/swcollector](https://github.com/gaochao1/swcollector) 可以自动探测网络设备型号,获取 CPU、内存使用率,当然,还有各个网口的监控数据,这是通用的 oid 4 | 5 | ## Configuration 6 | 7 | 最核心的配置就是指定 IP 列表,有三种写法: 8 | 9 | ```toml 10 | [[instances]] 11 | ips = [ 12 | "172.16.2.1", 13 | "172.16.4/24", 14 | "192.168.56.102-192.168.56.120" 15 | ] 16 | ``` 17 | 18 | 该插件只支持 SNMP v2c,所以认证信息就是一个 community 字符串 19 | 20 | ## 唯一标识标签 21 | 22 | 网络设备的监控数据,默认都会带有 ip 标签,指定监控数据来源于哪个设备,如果想把监控数据当做夜莺里的监控对象,让网络设备自动出现在夜莺的监控对象表格里,只需要把 switch_id_label 设置为 ident 即可,这样一来,网络设备的 IP 信息会作为 ident 标签的值上报,夜莺会自动读取 ident 标签的值入库 23 | 24 | ## 名称映射 25 | 26 | 有时,我们看到网络设备的 IP,无法分辨是具体哪个设备,此时可以给 IP 一个映射名称: 27 | 28 | ```ini 29 | [mappings] 30 | "192.168.88.160" = "switch001.bj" 31 | "192.168.88.161" = "switch002.bj" 32 | ``` 33 | 34 | 这样一来,上报的监控数据就不用 IP 做标识了,而是使用 switch001.bj 这样的字符串做标识,更易读一些 35 | 36 | ## 自定义 oid 37 | 38 | `[[instances.customs]]` 部分可以配置多个,表示自定义 oid,默认情况下,该插件采集的都是设备各个网口的监控数据以及CPU和内存的使用率,如果要采集别的 oid,就需要使用这个自定义功能 39 | 40 | ## 监控大盘 41 | 42 | 社区有小伙伴帮忙做了一个监控大盘,就在该 README 同级目录下,大家可以导入夜莺使用 -------------------------------------------------------------------------------- /integrations/Systemd/collect/systemd/systemd.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | enable=false # 设置为true 打开采集 5 | #unit_include=".+" 6 | #unit_exclude="" 7 | enable_start_time_metrics=true #是否采集service unit的启动时间信息 单位秒 8 | enable_task_metrics=true # 是否采集service unit task的metrics 9 | enable_restarts_metrics=true #是否采集service unit重启的次数信息 10 | -------------------------------------------------------------------------------- /integrations/Systemd/icon/systemd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Systemd/icon/systemd.png -------------------------------------------------------------------------------- /integrations/Systemd/markdown/README.md: -------------------------------------------------------------------------------- 1 | # systemd 插件 2 | 自 [node_exporter](https://github.com/prometheus/node_exporter/blob/master/collector/systemd_linux.go) fork 并改动 3 | 4 | ## Configuration 5 | ```toml 6 | enable=false # 设置为true 打开采集 7 | #unit_include=".+" 8 | #unit_exclude="" 9 | enable_start_time_metrics=true #是否采集service unit的启动时间信息 单位秒 10 | enable_task_metrics=true # 是否采集service unit task的metrics 11 | enable_restarts_metrics=true #是否采集service unit重启的次数信息 12 | ``` 13 | -------------------------------------------------------------------------------- /integrations/TDEngine/icon/tdengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/TDEngine/icon/tdengine.png -------------------------------------------------------------------------------- /integrations/TDEngine/markdown/README.md: -------------------------------------------------------------------------------- 1 | # TDEngine 2 | 3 | TDEngine 也可以暴露 Prometheus 的监控数据,具体启用方法如下: 4 | 5 | TODO 6 | 7 | ## 采集配置 8 | 9 | 既然暴露了 Prometheus 协议的监控数据,那通过 categraf prometheus 插件直接采集即可。配置文件是 `conf/input.prometheus/prometheus.toml`。配置样例如下: 10 | 11 | ```toml 12 | [[instances]] 13 | urls = [ 14 | "http://192.168.11.177:8080/xxxx" 15 | ] 16 | ``` 17 | 18 | ## 仪表盘 19 | 20 | 夜莺内置了一个 TDEngine 仪表盘,由网友贡献,克隆到自己的业务组下即可使用,欢迎大家一起来提 PR 完善。 -------------------------------------------------------------------------------- /integrations/TiDB/icon/tidb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/TiDB/icon/tidb.png -------------------------------------------------------------------------------- /integrations/Tomcat/collect/tomcat/tomcat.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | # Gather metrics from the Tomcat server status page. 5 | [[instances]] 6 | ## URL of the Tomcat server status 7 | # url = "http://127.0.0.1:8080/manager/status/all?XML=true" 8 | url = "" 9 | 10 | ## HTTP Basic Auth Credentials 11 | # username = "tomcat" 12 | # password = "s3cret" 13 | 14 | ## Request timeout 15 | # timeout = "5s" 16 | 17 | # # interval = global.interval * interval_times 18 | # interval_times = 1 19 | 20 | # important! use global unique string to specify instance 21 | # labels = { instance="192.168.1.2:8080", url="-" } 22 | 23 | ## Optional TLS Config 24 | # use_tls = false 25 | # tls_min_version = "1.2" 26 | # tls_ca = "/etc/categraf/ca.pem" 27 | # tls_cert = "/etc/categraf/cert.pem" 28 | # tls_key = "/etc/categraf/key.pem" 29 | ## Use TLS but skip chain & host verification 30 | # insecure_skip_verify = true 31 | -------------------------------------------------------------------------------- /integrations/Tomcat/icon/tomcat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Tomcat/icon/tomcat.png -------------------------------------------------------------------------------- /integrations/VictoriaMetrics/icon/VictoriaMetrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/VictoriaMetrics/icon/VictoriaMetrics.png -------------------------------------------------------------------------------- /integrations/VictoriaMetrics/markdown/README.md: -------------------------------------------------------------------------------- 1 | # VictoriaMetrics 2 | 3 | VictoriaMetrics 既可以单机部署,也可以集群方式部署。不管哪种部署方式,VictoriaMetrics 的进程都会暴露 `/metrics` 接口,通过这个接口暴露 Prometheus 协议的监控数据。 4 | 5 | ## 采集配置 6 | 7 | categraf 的 `conf/input.prometheus/prometheus.toml`。因为 VictoriaMetrics 是暴露的 Prometheus 协议的监控数据,所以使用 categraf 的 prometheus 插件即可采集。 8 | 9 | ```toml 10 | # vmstorage 11 | [[instances]] 12 | urls = [ 13 | "http://127.0.0.1:8482/metrics" 14 | ] 15 | labels = {service="vmstorage"} 16 | 17 | # vmselect 18 | [[instances]] 19 | urls = [ 20 | "http://127.0.0.1:8481/metrics" 21 | ] 22 | 23 | labels = {service="vmselect"} 24 | 25 | # vminsert 26 | [[instances]] 27 | urls = [ 28 | "http://127.0.0.1:8480/metrics" 29 | ] 30 | labels = {service="vminsert"} 31 | ``` 32 | 33 | ## 告警规则 34 | 35 | 夜莺内置了 VictoriaMetrics 的告警规则,克隆到自己的业务组下即可使用。 36 | 37 | ## 仪表盘 38 | 39 | 夜莺内置了 VictoriaMetrics 的仪表盘,克隆到自己的业务组下即可使用。 40 | 41 | ![20230802090606](https://download.flashcat.cloud/ulric/20230802090606.png) 42 | 43 | -------------------------------------------------------------------------------- /integrations/VictoriaMetrics/markdown/alerts-vm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/VictoriaMetrics/markdown/alerts-vm.png -------------------------------------------------------------------------------- /integrations/VictoriaMetrics/markdown/alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/VictoriaMetrics/markdown/alerts.png -------------------------------------------------------------------------------- /integrations/VictoriaMetrics/markdown/dash-vm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/VictoriaMetrics/markdown/dash-vm.png -------------------------------------------------------------------------------- /integrations/VictoriaMetrics/markdown/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/VictoriaMetrics/markdown/dashboard.png -------------------------------------------------------------------------------- /integrations/Whois/collect/whois/whois.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | #interval = 3600 3 | 4 | #[[instances]] 5 | ## Used to collect domain name information. 6 | #domain = "baidu.com" 7 | 8 | ## append some labels for series 9 | #labels = { region="n9e", product="test1" } 10 | 11 | ## interval = global.interval * interval_times 12 | #interval_times = 1 13 | 14 | 15 | #[[instances]] 16 | ## Used to collect domain name information. 17 | #domain = "google.com" 18 | 19 | ## append some labels for series 20 | #labels = { region="n9e", product="test2" } 21 | 22 | ## interval = global.interval * interval_times 23 | #interval_times = 1 24 | 25 | -------------------------------------------------------------------------------- /integrations/Whois/icon/whois.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Whois/icon/whois.png -------------------------------------------------------------------------------- /integrations/Whois/markdown/README.md: -------------------------------------------------------------------------------- 1 | # whois 2 | 3 | 域名探测插件,用于探测域名的注册时间和到期时间,值为UTC0时间戳 4 | 5 | 6 | ## Configuration 7 | 8 | 最核心的配置就是 domain 配置,配置目标地址,比如想要监控一个地址: 9 | 默认保持注释状态,注释状态下,插件默认不启用 10 | 11 | ```toml 12 | # [[instances]] 13 | ## Used to collect domain name information. 14 | # domain = "baidu.com" 15 | ``` 16 | 请注意这里配置的是域名不是URL 17 | 18 | ## 指标解释 19 | 20 | whois_domain_createddate 域名创建时间戳 21 | whois_domain_updateddate 域名更新时间戳 22 | whois_domain_expirationdate 域名到期时间戳 23 | 24 | ## 注意事项 25 | 请不要将interval设置过短,会导致频繁请求timeout,没太大必要性,请尽量放长请求周期 -------------------------------------------------------------------------------- /integrations/Windows/icon/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Windows/icon/windows.png -------------------------------------------------------------------------------- /integrations/Windows/markdown/README.md: -------------------------------------------------------------------------------- 1 | # Windows 2 | 3 | categraf 不但支持 linux 监控数据采集,也支持 windows 监控数据采集,而且指标命名也是一样的,这样告警规则、仪表盘其实都可以复用。不需要对 windows 做额外处理。 4 | 5 | ## 安装 6 | 7 | categraf 在 windows 下安装请参考这个 [文档](https://flashcat.cloud/docs/content/flashcat-monitor/categraf/2-installation/)。 8 | 9 | ## 仪表盘 10 | 11 | linux、windows 仪表盘其实是可以复用的,只是两种操作系统个别指标不同。比如有些指标是 linux 特有的,有些指标是 windows 特有的。如果你想要分开查看,夜莺也内置了 windows 的仪表盘,克隆到自己的业务组下即可使用。 12 | 13 | ## 告警规则 14 | 15 | 夜莺虽然也内置了 windows 的告警规则,但因为 linux、windows 大部分指标都是一样的,就不建议为 windows 单独管理一份告警规则了。 16 | -------------------------------------------------------------------------------- /integrations/Windows/markdown/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/Windows/markdown/windows.png -------------------------------------------------------------------------------- /integrations/XSKYApi/collect/xskyapi/xskyapi.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | # 4 | [[instances]] 5 | # # append some labels for series 6 | # labels = { region="cloud", product="n9e" } 7 | 8 | # # interval = global.interval * interval_times 9 | # interval_times = 1 10 | 11 | ## must be one of oss/gfs/eus 12 | dss_type = "oss" 13 | 14 | ## URL of each server in the service's cluster 15 | servers = [ 16 | #"http://x.x.x.x:xx" 17 | ] 18 | 19 | ## Set response_timeout (default 5 seconds) 20 | response_timeout = "5s" 21 | 22 | xms_auth_tokens = [ 23 | #"xxxxxxxxxxxxxxx" 24 | ] 25 | 26 | -------------------------------------------------------------------------------- /integrations/XSKYApi/icon/xsky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/XSKYApi/icon/xsky.png -------------------------------------------------------------------------------- /integrations/XSKYApi/markdown/README.md: -------------------------------------------------------------------------------- 1 | # XSKY Api 2 | 3 | XSKY api 4 | 5 | ## Configations 6 | 7 | ```toml 8 | # # collect interval 9 | # interval = 15 10 | # 11 | [[instances]] 12 | # # append some labels for series 13 | # labels = { region="cloud", product="n9e" } 14 | 15 | # # interval = global.interval * interval_times 16 | # interval_times = 1 17 | 18 | ## must be one of oss/gfs/eus 19 | dss_type = "oss" 20 | 21 | ## URL of each server in the service's cluster 22 | servers = [ 23 | #"http://x.x.x.x:xx" 24 | ] 25 | 26 | ## Set response_timeout (default 5 seconds) 27 | response_timeout = "5s" 28 | 29 | xms_auth_tokens = [ 30 | #"xxxxxxxxxxxxxxx" 31 | ] 32 | 33 | 34 | ``` -------------------------------------------------------------------------------- /integrations/ZooKeeper/collect/zookeeper/zookeeper.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # cluster_name = "dev-zk-cluster" 6 | # addresses = "127.0.0.1:2181" 7 | # timeout = 10 8 | 9 | # important! use global unique string to specify instance 10 | # labels = { instance="n9e-10.2.3.4:2181" } 11 | 12 | ## Optional TLS Config 13 | # use_tls = false 14 | # tls_min_version = "1.2" 15 | # tls_ca = "/etc/categraf/ca.pem" 16 | # tls_cert = "/etc/categraf/cert.pem" 17 | # tls_key = "/etc/categraf/key.pem" 18 | ## Use TLS but skip chain & host verification 19 | # insecure_skip_verify = true -------------------------------------------------------------------------------- /integrations/ZooKeeper/icon/zookeeper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/ZooKeeper/icon/zookeeper.png -------------------------------------------------------------------------------- /integrations/ZooKeeper/markdown/README.md: -------------------------------------------------------------------------------- 1 | # zookeeper 2 | 3 | 注意: `>=3.6.0` zookeeper 版本内置 [prometheus 的支持](https://zookeeper.apache.org/doc/current/zookeeperMonitor.html),即,如果 zookeeper 启用了 prometheus,Categraf 可使用 prometheus 插件从这个 metrics 接口拉取数据即可。就无需使用 zookeeper 这个插件来采集了。 4 | 5 | ## 说明 6 | 7 | categraf zookeeper 采集插件移植于 [dabealu/zookeeper-exporter](https://github.com/dabealu/zookeeper-exporter),适用于 `<3.6.0` 版本的 zookeeper, 原理就是利用 Zookeper 提供的四字命令(The Four Letter Words)获取监控信息。 8 | 9 | 需要注意的是,在 zookeeper v3.4.10 以后添加了四字命令白名单,需要在 zookeeper 的配置文件 `zoo.cfg` 中新增白名单配置: 10 | 11 | ``` 12 | 4lw.commands.whitelist=mntr,ruok 13 | ``` 14 | 15 | ## 配置 16 | 17 | zookeeper 插件的配置在 `conf/input.zookeeper/zookeeper.toml` 集群中的多个实例地址请用空格分隔: 18 | 19 | ```toml 20 | [[instances]] 21 | cluster_name = "dev-zk-cluster" 22 | addresses = "127.0.0.1:2181" 23 | timeout = 10 24 | ``` 25 | 26 | 如果要监控多个 zookeeper 集群,就增加 instances 即可: 27 | 28 | ```toml 29 | [[instances]] 30 | cluster_name = "dev-zk-cluster" 31 | addresses = "127.0.0.1:2181" 32 | timeout = 10 33 | 34 | [[instances]] 35 | cluster_name = "test-zk-cluster" 36 | addresses = "127.0.0.1:2181 127.0.0.1:2182 127.0.0.1:2183" 37 | timeout = 10 38 | ``` 39 | 40 | ## 监控大盘和告警规则 41 | 42 | 夜莺内置了 zookeeper 的监控大盘和告警规则,克隆到自己的业务组下即可使用。虽说文件名带有 `by_exporter` 字样,没关系,可以在 categraf 中使用。 43 | 44 | -------------------------------------------------------------------------------- /integrations/cAdvisor/collect/cadvisor/cadvisor.toml: -------------------------------------------------------------------------------- 1 | # # collect interval 2 | # interval = 15 3 | 4 | [[instances]] 5 | # url = "https://1.2.3.4:10250" 6 | # type = "kubelet" 7 | ## url = "http://1.2.3.4:8080/metrics" 8 | ## type = "cadvisor" 9 | 10 | # url_label_key = "instance" 11 | # url_label_value = "{{.Host}}" 12 | # bearer_token_string = "eyJlonglongxxxx.eyJlonglongyyyy.oQsXlonglongZZZ" 13 | ## bearer_token_file = "/path/to/token/file" 14 | 15 | # ignore_label_keys = ["id","name", "container_label*"] 16 | ## choose_label_keys = ["id"] 17 | 18 | # timeout = "3s" 19 | 20 | # use_tls = true 21 | ## tls_min_version = "1.2" 22 | ## tls_ca = "/etc/categraf/ca.pem" 23 | ## tls_cert = "/etc/categraf/cert.pem" 24 | ## tls_key = "/etc/categraf/key.pem" 25 | ## Use TLS but skip chain & host verification 26 | ## insecure_skip_verify = true -------------------------------------------------------------------------------- /integrations/cAdvisor/icon/cadvisor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/cAdvisor/icon/cadvisor.png -------------------------------------------------------------------------------- /integrations/vSphere/icon/vsphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccfos/nightingale/2b448f738cc7de374bcb6f95a18d8358857a5159/integrations/vSphere/icon/vsphere.png -------------------------------------------------------------------------------- /memsto/memsto.go: -------------------------------------------------------------------------------- 1 | package memsto 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/toolkits/pkg/logger" 7 | ) 8 | 9 | // TODO 优化 exit 处理方式 10 | func exit(code int) { 11 | logger.Close() 12 | os.Exit(code) 13 | } 14 | -------------------------------------------------------------------------------- /memsto/stat.go: -------------------------------------------------------------------------------- 1 | package memsto 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | type Stats struct { 6 | GaugeCronDuration *prometheus.GaugeVec 7 | GaugeSyncNumber *prometheus.GaugeVec 8 | } 9 | 10 | func NewSyncStats() *Stats { 11 | GaugeCronDuration := prometheus.NewGaugeVec(prometheus.GaugeOpts{ 12 | Namespace: "n9e", 13 | Subsystem: "cron", 14 | Name: "duration", 15 | Help: "Cron method use duration, unit: ms.", 16 | }, []string{"name"}) 17 | 18 | GaugeSyncNumber := prometheus.NewGaugeVec(prometheus.GaugeOpts{ 19 | Namespace: "n9e", 20 | Subsystem: "cron", 21 | Name: "sync_number", 22 | Help: "Cron sync number.", 23 | }, []string{"name"}) 24 | 25 | prometheus.MustRegister( 26 | GaugeCronDuration, 27 | GaugeSyncNumber, 28 | ) 29 | 30 | return &Stats{ 31 | GaugeCronDuration: GaugeCronDuration, 32 | GaugeSyncNumber: GaugeSyncNumber, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /models/board_payload.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/ccfos/nightingale/v6/pkg/ctx" 7 | ) 8 | 9 | type BoardPayload struct { 10 | Id int64 `json:"id" gorm:"primaryKey"` 11 | Payload string `json:"payload"` 12 | } 13 | 14 | func (p *BoardPayload) TableName() string { 15 | return "board_payload" 16 | } 17 | 18 | func (p *BoardPayload) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { 19 | return DB(ctx).Model(p).Select(selectField, selectFields...).Updates(p).Error 20 | } 21 | 22 | func BoardPayloadGets(ctx *ctx.Context, ids []int64) ([]*BoardPayload, error) { 23 | if len(ids) == 0 { 24 | return nil, errors.New("empty ids") 25 | } 26 | 27 | var arr []*BoardPayload 28 | err := DB(ctx).Where("id in ?", ids).Find(&arr).Error 29 | return arr, err 30 | } 31 | 32 | func BoardPayloadGet(ctx *ctx.Context, id int64) (string, error) { 33 | payloads, err := BoardPayloadGets(ctx, []int64{id}) 34 | if err != nil { 35 | return "", err 36 | } 37 | 38 | if len(payloads) == 0 { 39 | return "", nil 40 | } 41 | 42 | return payloads[0].Payload, nil 43 | } 44 | 45 | func BoardPayloadSave(ctx *ctx.Context, id int64, payload string) error { 46 | var bp BoardPayload 47 | err := DB(ctx).Where("id = ?", id).Find(&bp).Error 48 | if err != nil { 49 | return err 50 | } 51 | 52 | if bp.Id > 0 { 53 | // already exists 54 | bp.Payload = payload 55 | return bp.Update(ctx, "payload") 56 | } 57 | 58 | return Insert(ctx, &BoardPayload{ 59 | Id: id, 60 | Payload: payload, 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /models/builtin_cate.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/pkg/ctx" 5 | ) 6 | 7 | type BuiltinCate struct { 8 | Id int64 `json:"id" gorm:"primaryKey"` 9 | Name string `json:"name"` 10 | UserId int64 `json:"user_id"` 11 | } 12 | 13 | func (b *BuiltinCate) TableName() string { 14 | return "builtin_cate" 15 | } 16 | 17 | // 创建 builtin_cate 18 | func (b *BuiltinCate) Create(c *ctx.Context) error { 19 | return Insert(c, b) 20 | } 21 | 22 | // 删除 builtin_cate 23 | func BuiltinCateDelete(c *ctx.Context, name string, userId int64) error { 24 | return DB(c).Where("name=? and user_id=?", name, userId).Delete(&BuiltinCate{}).Error 25 | } 26 | 27 | // 根据 userId 获取 builtin_cate 28 | func BuiltinCateGetByUserId(c *ctx.Context, userId int64) (map[string]BuiltinCate, error) { 29 | var builtinCates []BuiltinCate 30 | err := DB(c).Where("user_id=?", userId).Find(&builtinCates).Error 31 | var builtinCatesMap = make(map[string]BuiltinCate) 32 | for _, builtinCate := range builtinCates { 33 | builtinCatesMap[builtinCate.Name] = builtinCate 34 | } 35 | 36 | return builtinCatesMap, err 37 | } 38 | -------------------------------------------------------------------------------- /models/chart.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "github.com/ccfos/nightingale/v6/pkg/ctx" 4 | 5 | type Chart struct { 6 | Id int64 `json:"id" gorm:"primaryKey"` 7 | GroupId int64 `json:"group_id"` 8 | Configs string `json:"configs"` 9 | Weight int `json:"weight"` 10 | } 11 | 12 | func (c *Chart) TableName() string { 13 | return "chart" 14 | } 15 | 16 | func ChartsOf(ctx *ctx.Context, chartGroupId int64) ([]Chart, error) { 17 | var objs []Chart 18 | err := DB(ctx).Where("group_id = ?", chartGroupId).Order("weight").Find(&objs).Error 19 | return objs, err 20 | } 21 | 22 | func (c *Chart) Add(ctx *ctx.Context) error { 23 | return Insert(ctx, c) 24 | } 25 | 26 | func (c *Chart) Update(ctx *ctx.Context, selectField interface{}, selectFields ...interface{}) error { 27 | return DB(ctx).Model(c).Select(selectField, selectFields...).Updates(c).Error 28 | } 29 | 30 | func (c *Chart) Del(ctx *ctx.Context) error { 31 | return DB(ctx).Where("id=?", c.Id).Delete(&Chart{}).Error 32 | } 33 | -------------------------------------------------------------------------------- /models/chart_share.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "github.com/ccfos/nightingale/v6/pkg/ctx" 4 | 5 | type ChartShare struct { 6 | Id int64 `json:"id" gorm:"primaryKey"` 7 | Cluster string `json:"cluster"` 8 | DatasourceId int64 `json:"datasource_id"` 9 | Configs string `json:"configs"` 10 | CreateBy string `json:"create_by"` 11 | CreateAt int64 `json:"create_at"` 12 | } 13 | 14 | func (cs *ChartShare) TableName() string { 15 | return "chart_share" 16 | } 17 | 18 | func (cs *ChartShare) Add(ctx *ctx.Context) error { 19 | return Insert(ctx, cs) 20 | } 21 | 22 | func ChartShareGetsByIds(ctx *ctx.Context, ids []int64) ([]ChartShare, error) { 23 | var lst []ChartShare 24 | if len(ids) == 0 { 25 | return lst, nil 26 | } 27 | 28 | err := DB(ctx).Where("id in ?", ids).Order("id").Find(&lst).Error 29 | return lst, err 30 | } 31 | -------------------------------------------------------------------------------- /models/event_processor.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/ccfos/nightingale/v6/pkg/ctx" 8 | ) 9 | 10 | type Processor interface { 11 | Init(settings interface{}) (Processor, error) // 初始化配置 12 | Process(ctx *ctx.Context, event *AlertCurEvent) *AlertCurEvent // 处理告警事件 13 | } 14 | 15 | type NewProcessorFn func(settings interface{}) (Processor, error) 16 | 17 | var processorRegister = map[string]NewProcessorFn{} 18 | 19 | func RegisterProcessor(typ string, p Processor) { 20 | if _, found := processorRegister[typ]; found { 21 | return 22 | } 23 | processorRegister[typ] = p.Init 24 | } 25 | 26 | func GetProcessorByType(typ string, settings interface{}) (Processor, error) { 27 | typ = strings.TrimSpace(typ) 28 | fn, found := processorRegister[typ] 29 | if !found { 30 | return nil, fmt.Errorf("processor type %s not found", typ) 31 | } 32 | 33 | processor, err := fn(settings) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return processor, nil 39 | } 40 | -------------------------------------------------------------------------------- /models/migrate/migrate_es_index_pattern.go: -------------------------------------------------------------------------------- 1 | package migrate 2 | 3 | import ( 4 | "github.com/toolkits/pkg/logger" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type EsIndexPattern struct { 9 | Id int64 `gorm:"primaryKey;type:bigint unsigned"` 10 | DatasourceId int64 `gorm:"type:bigint not null default '0';uniqueIndex:idx_ds_name"` 11 | Name string `gorm:"type:varchar(191) not null default '';uniqueIndex:idx_ds_name"` 12 | TimeField string `gorm:"type:varchar(128) not null default ''"` 13 | AllowHideSystemIndices int `gorm:"type:tinyint(1) not null default 0"` 14 | FieldsFormat string `gorm:"type:varchar(4096) not null default ''"` 15 | CreateAt int64 `gorm:"type:bigint default '0'"` 16 | CreateBy string `gorm:"type:varchar(64) default ''"` 17 | UpdateAt int64 `gorm:"type:bigint default '0'"` 18 | UpdateBy string `gorm:"type:varchar(64) default ''"` 19 | } 20 | 21 | func MigrateEsIndexPatternTable(db *gorm.DB) error { 22 | db = db.Set("gorm:table_options", "CHARSET=utf8mb4") 23 | if db.Migrator().HasTable("es_index_pattern") { 24 | return nil 25 | } 26 | 27 | err := db.Table("es_index_pattern").AutoMigrate(&EsIndexPattern{}) 28 | if err != nil { 29 | logger.Errorf("failed to migrate es index pattern table: %v", err) 30 | return err 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /models/migrate/migrate_test.go: -------------------------------------------------------------------------------- 1 | package migrate 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/ccfos/nightingale/v6/models" 8 | "gorm.io/driver/mysql" 9 | "gorm.io/gorm" 10 | "gorm.io/gorm/schema" 11 | ) 12 | 13 | func TestInsertPermPoints(t *testing.T) { 14 | db, err := gorm.Open(mysql.Open("root:1234@tcp(127.0.0.1:3306)/n9e_v6?charset=utf8mb4&parseTime=True&loc=Local&allowNativePasswords=true"), &gorm.Config{NamingStrategy: schema.NamingStrategy{ 15 | SingularTable: true, 16 | }}) 17 | if err != nil { 18 | fmt.Printf("failed to connect database: %v", err) 19 | } 20 | 21 | var ops []models.RoleOperation 22 | ops = append(ops, models.RoleOperation{ 23 | RoleName: "Standard", 24 | Operation: "/alert-mutes/put", 25 | }) 26 | 27 | ops = append(ops, models.RoleOperation{ 28 | RoleName: "Standard", 29 | Operation: "/log/index-patterns", 30 | }) 31 | 32 | ops = append(ops, models.RoleOperation{ 33 | RoleName: "Standard", 34 | Operation: "/help/variable-configs", 35 | }) 36 | 37 | ops = append(ops, models.RoleOperation{ 38 | RoleName: "Standard", 39 | Operation: "/ibex-settings", 40 | }) 41 | 42 | db = db.Debug() 43 | for _, op := range ops { 44 | var count int64 45 | 46 | err := db.Raw("SELECT COUNT(*) FROM role_operation WHERE operation = ? AND role_name = ?", 47 | op.Operation, op.RoleName).Scan(&count).Error 48 | fmt.Printf("count: %d\n", count) 49 | 50 | if err != nil { 51 | fmt.Printf("check role operation exists failed, %v", err) 52 | continue 53 | } 54 | 55 | if count > 0 { 56 | continue 57 | } 58 | 59 | err = db.Create(&op).Error 60 | if err != nil { 61 | fmt.Printf("insert role operation failed, %v", err) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /models/role_operation.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/pkg/ctx" 5 | "github.com/toolkits/pkg/slice" 6 | ) 7 | 8 | type RoleOperation struct { 9 | RoleName string 10 | Operation string 11 | } 12 | 13 | func (RoleOperation) TableName() string { 14 | return "role_operation" 15 | } 16 | 17 | func RoleHasOperation(ctx *ctx.Context, roles []string, operation string) (bool, error) { 18 | if len(roles) == 0 { 19 | return false, nil 20 | } 21 | 22 | return Exists(DB(ctx).Model(&RoleOperation{}).Where("operation = ? and role_name in ?", operation, roles)) 23 | } 24 | 25 | func OperationsOfRole(ctx *ctx.Context, roles []string) ([]string, error) { 26 | session := DB(ctx).Model(&RoleOperation{}).Select("distinct(operation) as operation") 27 | 28 | if !slice.ContainsString(roles, AdminRole) { 29 | session = session.Where("role_name in ?", roles) 30 | } 31 | 32 | var ret []string 33 | err := session.Pluck("operation", &ret).Error 34 | return ret, err 35 | } 36 | 37 | func RoleOperationBind(ctx *ctx.Context, roleName string, operation []string) error { 38 | tx := DB(ctx).Begin() 39 | 40 | if err := tx.Where("role_name = ?", roleName).Delete(&RoleOperation{}).Error; err != nil { 41 | tx.Rollback() 42 | return err 43 | } 44 | 45 | if len(operation) == 0 { 46 | return tx.Commit().Error 47 | } 48 | 49 | var ops []RoleOperation 50 | for _, op := range operation { 51 | ops = append(ops, RoleOperation{ 52 | RoleName: roleName, 53 | Operation: op, 54 | }) 55 | } 56 | 57 | if err := tx.Create(&ops).Error; err != nil { 58 | tx.Rollback() 59 | return err 60 | } 61 | 62 | return tx.Commit().Error 63 | } 64 | -------------------------------------------------------------------------------- /models/source_token.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ccfos/nightingale/v6/pkg/ctx" 7 | ) 8 | 9 | type SourceToken struct { 10 | Id int64 `json:"id" gorm:"primaryKey"` 11 | SourceType string `json:"source_type" gorm:"column:source_type;type:varchar(64);not null;default:''"` 12 | SourceId string `json:"source_id" gorm:"column:source_id;type:varchar(255);not null;default:''"` 13 | Token string `json:"token" gorm:"column:token;type:varchar(255);not null;default:''"` 14 | ExpireAt int64 `json:"expire_at" gorm:"type:bigint;not null;default:0"` 15 | CreateAt int64 `json:"create_at" gorm:"type:bigint;not null;default:0"` 16 | CreateBy string `json:"create_by" gorm:"type:varchar(64);not null;default:''"` 17 | } 18 | 19 | func (SourceToken) TableName() string { 20 | return "source_token" 21 | } 22 | 23 | func (st *SourceToken) Add(ctx *ctx.Context) error { 24 | return Insert(ctx, st) 25 | } 26 | 27 | // GetSourceTokenBySource 根据源类型和源ID获取源令牌 28 | func GetSourceTokenBySource(ctx *ctx.Context, sourceType, sourceId, token string) (*SourceToken, error) { 29 | var st SourceToken 30 | err := DB(ctx).Where("source_type = ? AND source_id = ? AND token = ?", sourceType, sourceId, token).First(&st).Error 31 | if err != nil { 32 | return nil, err 33 | } 34 | return &st, nil 35 | } 36 | 37 | func (st *SourceToken) IsExpired() bool { 38 | if st.ExpireAt == 0 { 39 | return false // 0 表示永不过期 40 | } 41 | return time.Now().Unix() > st.ExpireAt 42 | } 43 | 44 | func CleanupExpiredTokens(ctx *ctx.Context) (int64, error) { 45 | now := time.Now().Unix() 46 | result := DB(ctx).Where("expire_at > 0 AND expire_at < ?", now).Delete(&SourceToken{}) 47 | return result.RowsAffected, result.Error 48 | } 49 | -------------------------------------------------------------------------------- /models/sso_config.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ccfos/nightingale/v6/pkg/ctx" 7 | ) 8 | 9 | type SsoConfig struct { 10 | Id int64 `json:"id"` 11 | Name string `json:"name"` 12 | Content string `json:"content"` 13 | UpdateAt int64 `json:"update_at"` 14 | } 15 | 16 | func (b *SsoConfig) TableName() string { 17 | return "sso_config" 18 | } 19 | 20 | // get all sso_config 21 | func SsoConfigGets(c *ctx.Context) ([]SsoConfig, error) { 22 | var lst []SsoConfig 23 | err := DB(c).Find(&lst).Error 24 | return lst, err 25 | } 26 | 27 | // 创建 builtin_cate 28 | func (b *SsoConfig) Create(c *ctx.Context) error { 29 | return Insert(c, b) 30 | } 31 | 32 | func (b *SsoConfig) Update(c *ctx.Context) error { 33 | b.UpdateAt = time.Now().Unix() 34 | return DB(c).Model(b).Select("content", "update_at").Updates(b).Error 35 | } 36 | 37 | // get sso_config last update time 38 | func SsoConfigLastUpdateTime(c *ctx.Context) (int64, error) { 39 | var lastUpdateTime int64 40 | err := DB(c).Model(&SsoConfig{}).Select("max(update_at)").Row().Scan(&lastUpdateTime) 41 | return lastUpdateTime, err 42 | } 43 | 44 | // get sso_config coutn by name 45 | func SsoConfigCountByName(c *ctx.Context, name string) (int64, error) { 46 | var count int64 47 | err := DB(c).Model(&SsoConfig{}).Where("name = ?", name).Count(&count).Error 48 | return count, err 49 | } 50 | -------------------------------------------------------------------------------- /pkg/cfg/scan.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | import ( 4 | "io/ioutil" 5 | ) 6 | 7 | type scanner struct { 8 | data []byte 9 | err error 10 | } 11 | 12 | func NewFileScanner() *scanner { 13 | return &scanner{} 14 | } 15 | 16 | func (s *scanner) Err() error { 17 | return s.err 18 | } 19 | 20 | func (s *scanner) Data() []byte { 21 | return s.data 22 | } 23 | 24 | func (s *scanner) Read(file string) { 25 | if s.err == nil { 26 | s.data, s.err = ioutil.ReadFile(file) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/choice/choice.go: -------------------------------------------------------------------------------- 1 | // Package choice provides basic functions for working with 2 | // plugin options that must be one of several values. 3 | package choice 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | // Contains return true if the choice in the list of choices. 11 | func Contains(choice string, choices []string) bool { 12 | for _, item := range choices { 13 | if item == choice { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | // Contains return true if the choice in the list of choices. 21 | func ContainsPrefix(choice string, choices []string) bool { 22 | for _, item := range choices { 23 | if strings.HasPrefix(choice, item) { 24 | return true 25 | } 26 | } 27 | return false 28 | } 29 | 30 | // Check returns an error if a choice is not one of 31 | // the available choices. 32 | func Check(choice string, available []string) error { 33 | if !Contains(choice, available) { 34 | return fmt.Errorf("unknown choice %s", choice) 35 | } 36 | return nil 37 | } 38 | 39 | // CheckSlice returns an error if the choices is not a subset of 40 | // available. 41 | func CheckSlice(choices, available []string) error { 42 | for _, choice := range choices { 43 | err := Check(choice, available) 44 | if err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/ctx/ctx.go: -------------------------------------------------------------------------------- 1 | package ctx 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ccfos/nightingale/v6/conf" 7 | 8 | "gorm.io/gorm" 9 | ) 10 | 11 | type Context struct { 12 | DB *gorm.DB 13 | CenterApi conf.CenterApi 14 | Ctx context.Context 15 | IsCenter bool 16 | } 17 | 18 | func NewContext(ctx context.Context, db *gorm.DB, isCenter bool, centerApis ...conf.CenterApi) *Context { 19 | var api conf.CenterApi 20 | if len(centerApis) > 0 { 21 | api = centerApis[0] 22 | } 23 | 24 | return &Context{ 25 | Ctx: ctx, 26 | DB: db, 27 | CenterApi: api, 28 | IsCenter: isCenter, 29 | } 30 | } 31 | 32 | // set db to Context 33 | func (c *Context) SetDB(db *gorm.DB) { 34 | c.DB = db 35 | } 36 | 37 | // get context from Context 38 | func (c *Context) GetContext() context.Context { 39 | return c.Ctx 40 | } 41 | 42 | // get db from Context 43 | func (c *Context) GetDB() *gorm.DB { 44 | return c.DB 45 | } 46 | -------------------------------------------------------------------------------- /pkg/fasttime/fasttime.go: -------------------------------------------------------------------------------- 1 | package fasttime 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | go func() { 10 | ticker := time.NewTicker(time.Second) 11 | defer ticker.Stop() 12 | for tm := range ticker.C { 13 | t := uint64(tm.Unix()) 14 | atomic.StoreUint64(¤tTimestamp, t) 15 | } 16 | }() 17 | } 18 | 19 | var currentTimestamp = uint64(time.Now().Unix()) 20 | 21 | // UnixTimestamp returns the current unix timestamp in seconds. 22 | // 23 | // It is faster than time.Now().Unix() 24 | func UnixTimestamp() uint64 { 25 | return atomic.LoadUint64(¤tTimestamp) 26 | } 27 | 28 | // UnixDate returns date from the current unix timestamp. 29 | // 30 | // The date is calculated by dividing unix timestamp by (24*3600) 31 | func UnixDate() uint64 { 32 | return UnixTimestamp() / (24 * 3600) 33 | } 34 | 35 | // UnixHour returns hour from the current unix timestamp. 36 | // 37 | // The hour is calculated by dividing unix timestamp by 3600 38 | func UnixHour() uint64 { 39 | return UnixTimestamp() / 3600 40 | } 41 | -------------------------------------------------------------------------------- /pkg/hash/hash.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | prommodel "github.com/prometheus/common/model" 8 | "github.com/spaolacci/murmur3" 9 | ) 10 | 11 | func GetHash(m prommodel.Metric, ref string) uint64 { 12 | var str string 13 | var strs []string 14 | // get keys from m 15 | for k, _ := range m { 16 | strs = append(strs, string(k)) 17 | } 18 | 19 | // sort keys use sort 20 | sort.Strings(strs) 21 | 22 | for _, k := range strs { 23 | str += "/" 24 | str += k 25 | str += "/" 26 | str += string(m[prommodel.LabelName(k)]) 27 | } 28 | str += "/" 29 | str += ref 30 | 31 | return murmur3.Sum64([]byte(str)) 32 | } 33 | 34 | func GetTagHash(m prommodel.Metric) uint64 { 35 | var str string 36 | var strs []string 37 | // get keys from m 38 | for k, _ := range m { 39 | if k == "__name__" { 40 | continue 41 | } 42 | strs = append(strs, string(k)) 43 | } 44 | 45 | // sort keys use sort 46 | sort.Strings(strs) 47 | 48 | for _, k := range strs { 49 | str += "/" 50 | str += k 51 | str += "/" 52 | str += string(m[prommodel.LabelName(k)]) 53 | } 54 | 55 | return murmur3.Sum64([]byte(str)) 56 | } 57 | 58 | func GetTargetTagHash(m prommodel.Metric, target []string) uint64 { 59 | builder := strings.Builder{} 60 | for _, k := range target { 61 | builder.WriteString("/") 62 | builder.WriteString(k) 63 | builder.WriteString("/") 64 | builder.WriteString(string(m[prommodel.LabelName(k)])) 65 | } 66 | return murmur3.Sum64([]byte(builder.String())) 67 | } 68 | -------------------------------------------------------------------------------- /pkg/hash/hash_fnv.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package hash 18 | 19 | import ( 20 | "hash" 21 | 22 | "github.com/davecgh/go-spew/spew" 23 | ) 24 | 25 | // DeepHashObject writes specified object to hash using the spew library 26 | // which follows pointers and prints actual values of the nested objects 27 | // ensuring the hash does not change when a pointer changes. 28 | func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) { 29 | hasher.Reset() 30 | printer := spew.ConfigState{ 31 | Indent: " ", 32 | SortKeys: true, 33 | DisableMethods: true, 34 | SpewKeys: true, 35 | } 36 | printer.Fprintf(hasher, "%#v", objectToWrite) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/hash/hash_md5.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | prommodel "github.com/prometheus/common/model" 5 | "github.com/toolkits/pkg/str" 6 | ) 7 | 8 | func GetHash2(m prommodel.Metric, ref string) string { 9 | var s string 10 | for k, v := range m { 11 | s += "/" 12 | s += string(k) 13 | s += "/" 14 | s += string(v) 15 | } 16 | s += "/" 17 | s += ref 18 | return str.MD5(s) 19 | } 20 | 21 | func GetTagHash2(m prommodel.Metric) string { 22 | var s string 23 | for k, v := range m { 24 | if k == "__name__" { 25 | continue 26 | } 27 | 28 | s += "/" 29 | s += string(k) 30 | s += "/" 31 | s += string(v) 32 | } 33 | return str.MD5(s) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/i18nx/i18n.go: -------------------------------------------------------------------------------- 1 | package i18nx 2 | 3 | import ( 4 | "encoding/json" 5 | "path" 6 | 7 | "github.com/toolkits/pkg/file" 8 | "github.com/toolkits/pkg/i18n" 9 | "github.com/toolkits/pkg/logger" 10 | ) 11 | 12 | func Init(configDir string) { 13 | filePath := path.Join(configDir, "i18n.json") 14 | m := make(map[string]map[string]string) 15 | builtInConf := make(map[string]map[string]string) 16 | 17 | var content = I18N 18 | var err error 19 | //use built-in config 20 | err = json.Unmarshal([]byte(content), &builtInConf) 21 | if err != nil { 22 | logger.Errorf("parse i18n config file %s fail: %s\n", filePath, err) 23 | return 24 | } 25 | if !file.IsExist(filePath) { 26 | m = builtInConf 27 | } else { 28 | //expand config 29 | //prioritize the settings within the expand config options in case of conflicts 30 | content, err = file.ToTrimString(filePath) 31 | if err != nil { 32 | logger.Errorf("read i18n config file %s fail: %s\n", filePath, err) 33 | return 34 | } 35 | err = json.Unmarshal([]byte(content), &m) 36 | if err != nil { 37 | logger.Errorf("parse i18n config file %s fail: %s\n", filePath, err) 38 | return 39 | } 40 | // json Example: 41 | //{ 42 | // "zh": { 43 | // "username":"用户名" 44 | // }, 45 | // "fr": { 46 | // "username":"nom d'utilisateur" 47 | // } 48 | //} 49 | for languageKey, languageDict := range builtInConf { 50 | if _, hasL := m[languageKey]; hasL { //languages 51 | for k, v := range languageDict { 52 | if _, has := m[languageKey][k]; !has { 53 | m[languageKey][k] = v 54 | } 55 | } 56 | } else { 57 | m[languageKey] = languageDict 58 | } 59 | } 60 | } 61 | 62 | i18n.DictRegister(m) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/logx/logx.go: -------------------------------------------------------------------------------- 1 | package logx 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pkg/errors" 7 | "github.com/toolkits/pkg/logger" 8 | ) 9 | 10 | type Config struct { 11 | Dir string 12 | Level string 13 | Output string 14 | KeepHours uint 15 | RotateNum int 16 | RotateSize uint64 17 | OutputToOneFile bool 18 | } 19 | 20 | func Init(c Config) (func(), error) { 21 | logger.SetSeverity(c.Level) 22 | 23 | if c.Output == "stderr" { 24 | logger.LogToStderr() 25 | } else if c.Output == "file" { 26 | lb, err := logger.NewFileBackend(c.Dir) 27 | if err != nil { 28 | return nil, errors.WithMessage(err, "NewFileBackend failed") 29 | } 30 | 31 | if c.KeepHours != 0 { 32 | lb.SetRotateByHour(true) 33 | lb.SetKeepHours(c.KeepHours) 34 | } else if c.RotateNum != 0 { 35 | lb.Rotate(c.RotateNum, c.RotateSize*1024*1024) 36 | } else { 37 | return nil, errors.New("KeepHours and Rotatenum both are 0") 38 | } 39 | lb.OutputToOneFile(c.OutputToOneFile) 40 | 41 | logger.SetLogging(c.Level, lb) 42 | } 43 | 44 | return func() { 45 | fmt.Println("logger exiting") 46 | logger.Close() 47 | }, nil 48 | } 49 | -------------------------------------------------------------------------------- /pkg/macros/macros.go: -------------------------------------------------------------------------------- 1 | package macros 2 | 3 | var Macro func(sql string, start, end int64) (string, error) 4 | 5 | func RegisterMacro(f func(sql string, start, end int64) (string, error)) { 6 | Macro = f 7 | } 8 | 9 | func MacroInVain(sql string, start, end int64) (string, error) { 10 | return sql, nil 11 | } 12 | -------------------------------------------------------------------------------- /pkg/osx/osx.go: -------------------------------------------------------------------------------- 1 | package osx 2 | 3 | import "os" 4 | 5 | // GetEnv returns the value of an environment variable, or returns the provided fallback value 6 | func GetEnv(key, fallback string) string { 7 | if value, ok := os.LookupEnv(key); ok { 8 | return value 9 | } 10 | return fallback 11 | } 12 | -------------------------------------------------------------------------------- /pkg/poster/post_test.go: -------------------------------------------------------------------------------- 1 | package poster 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/ccfos/nightingale/v6/conf" 10 | "github.com/ccfos/nightingale/v6/pkg/ctx" 11 | ) 12 | 13 | func TestPostByUrls(t *testing.T) { 14 | 15 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 16 | response := DataResponse[interface{}]{Dat: "", Err: ""} 17 | json.NewEncoder(w).Encode(response) 18 | })) 19 | defer server.Close() 20 | 21 | ctx := &ctx.Context{ 22 | CenterApi: conf.CenterApi{ 23 | Addrs: []string{server.URL}, 24 | }} 25 | 26 | if err := PostByUrls(ctx, "/v1/n9e/server-heartbeat", map[string]string{"a": "aa"}); err != nil { 27 | t.Errorf("PostByUrls() error = %v ", err) 28 | } 29 | } 30 | 31 | func TestPostByUrlsWithResp(t *testing.T) { 32 | 33 | expected := int64(123) 34 | 35 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 36 | response := DataResponse[int64]{Dat: expected, Err: ""} 37 | json.NewEncoder(w).Encode(response) 38 | })) 39 | defer server.Close() 40 | 41 | ctx := &ctx.Context{ 42 | CenterApi: conf.CenterApi{ 43 | Addrs: []string{server.URL}, 44 | }} 45 | 46 | gotT, err := PostByUrlsWithResp[int64](ctx, "/v1/n9e/event-persist", map[string]string{"b": "bb"}) 47 | if err != nil { 48 | t.Errorf("PostByUrlsWithResp() error = %v", err) 49 | return 50 | } 51 | if gotT != expected { 52 | t.Errorf("PostByUrlsWithResp() gotT = %v,expected = %v", gotT, expected) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /pkg/prom/client_option.go: -------------------------------------------------------------------------------- 1 | package prom 2 | 3 | type ClientOptions struct { 4 | Url string 5 | BasicAuthUser string 6 | BasicAuthPass string 7 | Headers []string 8 | } 9 | -------------------------------------------------------------------------------- /pkg/slice/contains.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | func HaveIntersection[T comparable](slice1, slice2 []T) bool { 4 | elemMap := make(map[T]bool) 5 | for _, val := range slice1 { 6 | elemMap[val] = true 7 | } 8 | 9 | for _, val := range slice2 { 10 | if elemMap[val] { 11 | return true 12 | } 13 | } 14 | return false 15 | } 16 | -------------------------------------------------------------------------------- /pkg/strx/verify.go: -------------------------------------------------------------------------------- 1 | package strx 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/toolkits/pkg/errorx" 10 | ) 11 | 12 | func IsValidURL(url string) bool { 13 | re := regexp.MustCompile(`^https?://[^\s/$.?#].[^\s]*$`) 14 | return re.MatchString(url) 15 | } 16 | 17 | func IdsInt64ForAPI(ids string, sep ...string) []int64 { 18 | if ids == "" { 19 | return []int64{} 20 | } 21 | 22 | s := "," 23 | if len(sep) > 0 { 24 | s = sep[0] 25 | } 26 | 27 | var arr []string 28 | 29 | if s == " " { 30 | arr = strings.Fields(ids) 31 | } else { 32 | arr = strings.Split(ids, s) 33 | } 34 | 35 | count := len(arr) 36 | ret := make([]int64, 0, count) 37 | for i := 0; i < count; i++ { 38 | if arr[i] != "" { 39 | id, err := strconv.ParseInt(arr[i], 10, 64) 40 | if err != nil { 41 | errorx.Bomb(http.StatusBadRequest, "cannot convert %s to int64", arr[i]) 42 | } 43 | 44 | ret = append(ret, id) 45 | } 46 | } 47 | 48 | return ret 49 | } 50 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | 7 | "github.com/hashicorp/go-version" 8 | "github.com/toolkits/pkg/logger" 9 | "github.com/toolkits/pkg/net/httplib" 10 | ) 11 | 12 | var Version = "unknown" 13 | var GithubVersion atomic.Value 14 | 15 | func CompareVersion(v1, v2 string) (int, error) { 16 | version1, err := version.NewVersion(v1) 17 | if err != nil { 18 | return 0, err 19 | } 20 | version2, err := version.NewVersion(v2) 21 | if err != nil { 22 | return 0, err 23 | } 24 | 25 | if version1.LessThan(version2) { 26 | return -1, nil 27 | } 28 | if version1.GreaterThan(version2) { 29 | return 1, nil 30 | } 31 | return 0, nil 32 | } 33 | 34 | func GetGithubVersion() { 35 | for { 36 | req := httplib.Get("https://api.github.com/repos/ccfos/nightingale/releases/latest") 37 | var release GithubRelease 38 | err := req.ToJSON(&release) 39 | if err != nil { 40 | logger.Errorf("get github version fail: %v", err) 41 | } 42 | 43 | GithubVersion.Store(release.TagName) 44 | time.Sleep(24 * time.Hour) 45 | } 46 | } 47 | 48 | type GithubRelease struct { 49 | TagName string `json:"tag_name"` 50 | } 51 | -------------------------------------------------------------------------------- /pushgw/router/router_target.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/pushgw/idents" 5 | "github.com/gin-gonic/gin" 6 | "github.com/toolkits/pkg/ginx" 7 | ) 8 | 9 | func (rt *Router) targetUpdate(c *gin.Context) { 10 | var f idents.TargetUpdate 11 | ginx.BindJSON(c, &f) 12 | 13 | m := make(map[string]struct{}) 14 | for _, ident := range f.Lst { 15 | m[ident] = struct{}{} 16 | } 17 | 18 | rt.IdentSet.MSet(m) 19 | ginx.NewRender(c).Message(nil) 20 | } 21 | -------------------------------------------------------------------------------- /pushgw/router/vars.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | var globalCounter uint64 4 | -------------------------------------------------------------------------------- /pushgw/writer/json/json-iterator.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "math" 5 | "unsafe" 6 | 7 | jsoniter "github.com/json-iterator/go" 8 | ) 9 | 10 | func init() { 11 | // 为了处理prom数据中的NaN值 12 | jsoniter.RegisterTypeEncoderFunc("float64", func(ptr unsafe.Pointer, stream *jsoniter.Stream) { 13 | f := *(*float64)(ptr) 14 | if math.IsNaN(f) { 15 | stream.WriteString("null") 16 | } else { 17 | stream.WriteFloat64(f) 18 | } 19 | }, func(ptr unsafe.Pointer) bool { 20 | return true 21 | }) 22 | } 23 | 24 | func MarshalWithCustomFloat(items interface{}) ([]byte, error) { 25 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 26 | return json.Marshal(items) 27 | } 28 | -------------------------------------------------------------------------------- /storage/redis_test.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/alicebob/miniredis/v2" 8 | "github.com/redis/go-redis/v9" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestMiniRedisMGet(t *testing.T) { 13 | s, err := miniredis.Run() 14 | if err != nil { 15 | t.Fatalf("failed to start miniredis: %v", err) 16 | } 17 | defer s.Close() 18 | 19 | rdb := redis.NewClient(&redis.Options{ 20 | Addr: s.Addr(), 21 | }) 22 | 23 | err = rdb.Ping(context.Background()).Err() 24 | if err != nil { 25 | t.Fatalf("failed to ping miniredis: %v", err) 26 | } 27 | 28 | mp := make(map[string]interface{}) 29 | mp["key1"] = "value1" 30 | mp["key2"] = "value2" 31 | mp["key3"] = "value3" 32 | 33 | err = MSet(context.Background(), rdb, mp) 34 | if err != nil { 35 | t.Fatalf("failed to set miniredis value: %v", err) 36 | } 37 | 38 | ctx := context.Background() 39 | keys := []string{"key1", "key2", "key3", "key4"} 40 | vals := MGet(ctx, rdb, keys) 41 | 42 | expected := [][]byte{[]byte("value1"), []byte("value2"), []byte("value3")} 43 | assert.Equal(t, expected, vals) 44 | } 45 | -------------------------------------------------------------------------------- /storage/storage.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "github.com/ccfos/nightingale/v6/pkg/ormx" 5 | 6 | "gorm.io/gorm" 7 | ) 8 | 9 | func New(cfg ormx.DBConfig) (*gorm.DB, error) { 10 | db, err := ormx.New(cfg) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | return db, nil 16 | } 17 | --------------------------------------------------------------------------------