├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── build.cmd ├── collector ├── bgwriter_state.go ├── cluster_state.go ├── collector.go ├── connections.go ├── connections_detail.go ├── database_size.go ├── diskspace.go ├── dynamic_memory.go ├── error.go ├── exporter_metrics.go ├── locks.go ├── max_connections.go ├── queries.go ├── scrapers.go ├── segment.go ├── segment_utils.go ├── system.go └── users.go ├── doc └── DEMO.PNG ├── docker-build.sh ├── go.mod ├── go.sum ├── grafana └── greenplum_dashboard.json ├── main.go └── stopwatch └── stopwatch.go /.gitignore: -------------------------------------------------------------------------------- 1 | ### VS Code ### 2 | .vscode/ 3 | 4 | ### IntelliJ IDEA ### 5 | .idea 6 | *.iws 7 | *.iml 8 | *.ipr 9 | 10 | ### 11 | bin/* 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #alpine build 2 | FROM golang:1.16.2-alpine AS builder 3 | 4 | RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct 5 | 6 | WORKDIR /home 7 | 8 | ADD . . 9 | 10 | RUN mkdir bin && go mod download && go build -o ./bin/greenplum_exporter 11 | 12 | # image 13 | FROM alpine:latest 14 | 15 | COPY --from=builder /home/bin/greenplum_exporter /home/greenplum_exporter 16 | 17 | EXPOSE 9297 18 | 19 | USER root 20 | 21 | CMD [ "/home/greenplum_exporter" , "--log.level=error"] 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | NOTHING! -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECTNAME=$(shell basename "$(PWD)") 2 | 3 | # Make is verbose in Linux. Make it silent. 4 | MAKEFLAGS += --silent 5 | 6 | .PHONY: clean 7 | clean: 8 | @echo " > Cleaning build cache..." 9 | go clean 10 | rm -f bin/greenplum_exporter 11 | rm -fr bin/dist 12 | 13 | .PHONY: build 14 | build: 15 | @echo " > Building binary..." 16 | if [ ! -d bin/ ]; then mkdir bin/ ; fi; 17 | go mod download && go build -o ./bin/greenplum_exporter 18 | 19 | .PHONY: package 20 | package: 21 | @echo " > Archive binary target files and srcipts..." 22 | if [ ! -d bin/ ]; then mkdir bin/ ; fi; 23 | cd bin/ && mkdir -p dist && mkdir -p tmp && cd - 24 | go build -o ./bin/tmp/greenplum_exporter 25 | cd bin/tmp/ && tar -czvf ../dist/greenplum_exporter.tar.gz * && cd - 26 | cd bin/ && rm -fr tmp/ && cd - 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Greenplum-exporter 2 | 3 | 基于go语言为Greenplum集成普罗米修斯(prometheus)的监控数据采集器。 4 | 5 | **项目地址:** 6 | 7 | - Github: https://github.com/tangyibo/greenplum_exporter 8 | 9 | - Gitee: https://gitee.com/inrgihc/greenplum_exporter 10 | 11 | ### 一、编译方法 12 | 13 | - centos系统下编译 14 | 15 | (1) 环境安装 16 | ``` 17 | wget https://gomirrors.org/dl/go/go1.14.12.linux-amd64.tar.gz 18 | tar -C /usr/local -xzf go1.14.12.linux-amd64.tar.gz 19 | export PATH=$PATH:/usr/local/go/bin 20 | go env -w GO111MODULE=on 21 | go env -w GOPROXY=https://goproxy.io,direct 22 | ``` 23 | 24 | (2) 软件编译 25 | ``` 26 | git clone https://github.com/tangyibo/greenplum_exporter 27 | cd greenplum_exporter/ && make build 28 | cd bin && ls -l 29 | ``` 30 | 31 | - docker环境下编译 32 | 33 | ``` 34 | git clone https://github.com/tangyibo/greenplum_exporter 35 | cd greenplum_exporter/ 36 | sh docker-build.sh 37 | ``` 38 | 39 | ### 二、 启动采集器 40 | 41 | - centos系统下 42 | 43 | ``` 44 | export GPDB_DATA_SOURCE_URL=postgres://gpadmin:password@10.17.20.11:5432/postgres?sslmode=disable 45 | ./greenplum_exporter --web.listen-address="0.0.0.0:9297" --web.telemetry-path="/metrics" --log.level=error 46 | ``` 47 | 48 | - docker运行 49 | 50 | ``` 51 | docker run -d -p 9297:9297 -e GPDB_DATA_SOURCE_URL=postgres://gpadmin:password@10.17.20.11:5432/postgres?sslmode=disable inrgihc/greenplum-exporter:latest 52 | ``` 53 | 54 | 注:环境变量GPDB_DATA_SOURCE_URL指定了连接Greenplum数据库的连接串(请使用gpadmin账号连接postgres库),该连接串以postgres://为前缀,具体格式如下: 55 | ``` 56 | postgres://gpadmin:password@10.17.20.11:5432/postgres?sslmode=disable 57 | postgres://[数据库连接账号,必须为gpadmin]:[账号密码,即gpadmin的密码]@[数据库的IP地址]:[数据库端口号]/[数据库名称,必须为postgres]?[参数名]=[参数值]&[参数名]=[参数值] 58 | ``` 59 | 60 | 然后访问监控指标的URL地址: *http://127.0.0.1:9297/metrics* 61 | 62 | 更多启动参数: 63 | 64 | ``` 65 | usage: greenplum_exporter [] 66 | 67 | Flags: 68 | -h, --help Show context-sensitive help (also try --help-long and --help-man). 69 | --web.listen-address="0.0.0.0:9297" 70 | web endpoint 71 | --web.telemetry-path="/metrics" 72 | Path under which to expose metrics. 73 | --disableDefaultMetrics do not report default metrics(go metrics and process metrics) 74 | --version Show application version. 75 | --log.level="info" Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal] 76 | --log.format="logger:stderr" 77 | Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true" 78 | 79 | ``` 80 | 81 | ### 三、支持的监控指标 82 | 83 | | No. | 指标名称 | 类型 | 标签组 | 度量单位 | 指标描述 | 数据源获取方法 | 84 | |:----:|:----|:----|:----|:----|:----|:----| 85 | | 1 | greenplum_cluster_state | Gauge| version; master(master主机名);standby(standby主机名) | boolean | gp 可达状态 ?:1→ 可用;0→ 不可用 | SELECT count(\*) from gp_dist_random('gp_id'); select version(); SELECT hostname from p_segment_configuration where content=-1 and role='p'; | 86 | | 2 | greenplum_cluster_uptime | Gauge | - | int | 启动持续的时间 | select extract(epoch from now() - pg_postmaster_start_time()); | 87 | | 3 | greenplum_cluster_sync | Gauge | - | int | Master同步Standby状态? 1→ 正常;0→ 异常 | SELECT count(*) from pg_stat_replication where state='streaming' | 88 | | 4 | greenplum_cluster_max_connections | Gauge | - | int | 最大连接个数 | show max_connections; show superuser_reserved_connections; | 89 | | 5 | greenplum_cluster_total_connections | Gauge | - | int | 当前连接个数 | select count(\*) total, count(\*) filter(where current_query='') idle, count(\*) filter(where current_query<>'') active, count(\*) filter(where current_query<>'' and not waiting) running, count(\*) filter(where current_query<>'' and waiting) waiting from pg_stat_activity where procpid <> pg_backend_pid(); | 90 | | 6 | greenplum_cluster_idle_connections | Gauge| - | int | idle连接数 | 同上 | 91 | | 7 | greenplum_cluster_active_connections | Gauge | - | int | active query | 同上 | 92 | | 8 | greenplum_cluster_running_connections | Gauge | - | int | query executing | 同上 | 93 | | 9 | greenplum_cluster_waiting_connections | Gauge | - | int | query waiting execute | 同上 | 94 | | 10 | greenplum_node_segment_status | Gauge | hostname; address; dbid; content; preferred_role; port; replication_port | int | segment的状态status: 1(U)→ up; 0(D)→ down | select * from gp_segment_configuration; | 95 | | 11 | greenplum_node_segment_role | Gauge | hostname; address; dbid; content; preferred_role; port; replication_port | int | segment的role角色: 1(P)→ primary; 2(M)→ mirror | 同上 | 96 | | 12 | greenplum_node_segment_mode | Gauge | hostname; address; dbid; content; preferred_role; port; replication_port | int | segment的mode:1(S)→ Synced; 2(R)→ Resyncing; 3(C)→ Change Tracking; 4(N)→ Not Syncing | 同上| 97 | | 13 | greenplum_node_segment_disk_free_mb_size | Gauge | hostname | MB | segment主机磁盘空间剩余大小(MB) | SELECT dfhostname as segment_hostname,sum(dfspace)/count(dfspace)/(1024*1024) as segment_disk_free_gb from gp_toolkit.gp_disk_free GROUP BY dfhostname| 98 | | 14 | greenplum_cluster_total_connections_per_client | Gauge | client | int | 每个客户端的total连接数 |select usename, count(*) total, count(*) filter(where current_query='') idle, count(*) filter(where current_query<>'') active from pg_stat_activity group by 1; | 99 | | 15 | greenplum_cluster_idle_connections_per_client | Gauge | client | int | 每个客户端的idle连接数 | 同上 | 100 | | 16 | greenplum_cluster_active_connections_per_client | Gauge | client | int | 每个客户端的active连接数 | 同上 | 101 | | 17 | greenplum_cluster_total_online_user_count | Gauge | - | int | 在线账号数 | 同上 | 102 | | 18 | greenplum_cluster_total_client_count | Gauge | - | int | 当前所有连接的客户端个数 | 同上 | 103 | | 19 | greenplum_cluster_total_connections_per_user | Gauge | usename | int | 每个账号的total连接数 | select client_addr, count(*) total, count(*) filter(where current_query='') idle, count(*) filter(where current_query<>'') active from pg_stat_activity group by 1; | 104 | | 20 | greenplum_cluster_idle_connections_per_user | Gauge | usename | int | 每个账号的idle连接数 | 同上 | 105 | | 21 | greenplum_cluster_active_connections_per_user | Gauge | usename | int | 每个账号的active连接数 | 同上 | 106 | | 22 | greenplum_cluster_config_last_load_time_seconds | Gauge | - | int | 系统配置加载时间 | SELECT pg_conf_load_time() | 107 | | 23 | greenplum_node_database_name_mb_size | Gauge | dbname | MB | 每个数据库占用的存储空间大小 | SELECT dfhostname as segment_hostname,sum(dfspace)/count(dfspace)/(1024*1024) as segment_disk_free_gb from gp_toolkit.gp_disk_free GROUP BY dfhostname | 108 | | 24 | greenplum_node_database_table_total_count | Gauge | dbname | - | 每个数据库内表的总数量 | SELECT count(*) as total from information_schema.tables where table_schema not in ('gp_toolkit','information_schema','pg_catalog'); | 109 | | 25 | greenplum_exporter_total_scraped | Counter | -| int | - | - | 110 | | 26 | greenplum_exporter_total_error | Counter | - | int | - | - | 111 | | 27 | greenplum_exporter_scrape_duration_second | Gauge | - | int | - | - | 112 | | 28 | greenplum_server_users_name_list | Gauge | - | int | 用户总数 | SELECT usename from pg_catalog.pg_user; | 113 | | 29 | greenplum_server_users_total_count | Gauge | - | int | 用户明细 | 同上 | 114 | | 30 | greenplum_server_locks_table_detail | Gauge | pid;datname;usename;locktype;mode;application_name;state;lock_satus;query | int | 锁信息 | SELECT * from pg_locks | 115 | | 31 | greenplum_server_database_hit_cache_percent_rate | Gauge | - | float | 缓存命中率 | select sum(blks_hit)/(sum(blks_read)+sum(blks_hit))*100 from pg_stat_database; | 116 | | 32 | greenplum_server_database_transition_commit_percent_rate | Gauge | - | float | 事务提交率 | select sum(xact_commit)/(sum(xact_commit)+sum(xact_rollback))*100 from pg_stat_database; | 117 | | 32 | greenplum_server_database_table_bloat_list | Gauge | - | int | 数据膨胀列表 | select * from gp_toolkit.gp_bloat_diag; | 118 | | 33 | greenplum_server_database_table_skew_list | Gauge | - | int | 数据倾斜列表 | select * from gp_toolkit.gp_skew_coefficients; | 119 | 120 | ### 四、Grafana图 121 | 122 | - Dashboard 123 | 124 | Grafana Dashboard ID: 13822 125 | 126 | Grafana Dashboard URL: https://grafana.com/grafana/dashboards/13822 127 | 128 | - 配置教程 129 | 130 | 可参考文章:https://blog.csdn.net/inrgihc/article/details/108686638 131 | 132 | ### 五、问题反馈 133 | 134 | 如果您看到或使用了本工具,或您觉得本工具对您有价值,请为此项目**点个赞**!! 135 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :: 2 | :: Author: tang 3 | :: Date: 2020-03-06 4 | :: 5 | @echo off 6 | title greenplum_exporter 7 | setlocal enabledelayedexpansion 8 | cls 9 | 10 | set binFolder=bin\ 11 | 12 | if not exist %binFolder% ( 13 | md %binFolder% 14 | ) 15 | 16 | go clean 17 | go mod download 18 | go build -o %binFolder%\greenplum_exporter.exe 19 | 20 | echo "build over!" 21 | :exit 22 | pause 23 | -------------------------------------------------------------------------------- /collector/bgwriter_state.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "github.com/prometheus/client_golang/prometheus" 7 | logger "github.com/prometheus/common/log" 8 | "time" 9 | ) 10 | 11 | // 参考地址: 12 | // (1) https://zhmin.github.io/2019/11/27/postgresql-bg-writer/ 13 | // (2) https://zhmin.github.io/2019/11/24/postgresql-checkpoint/ 14 | const ( 15 | statBgwriterSql_V6 = ` SELECT checkpoints_timed, checkpoints_req, checkpoint_write_time, checkpoint_sync_time, buffers_checkpoint 16 | , buffers_clean, maxwritten_clean, buffers_backend, buffers_backend_fsync, buffers_alloc, stats_reset FROM pg_stat_bgwriter` 17 | statBgwriterSql_V5 = ` SELECT checkpoints_timed, checkpoints_req, 0 as checkpoint_write_time, 0 as checkpoint_sync_time, buffers_checkpoint 18 | , buffers_clean, maxwritten_clean, buffers_backend, 0 as buffers_backend_fsync, buffers_alloc, '2020-06-16 22:09:47.078+08'::timestamp as stats_reset FROM pg_stat_bgwriter;` 19 | ) 20 | 21 | var ( 22 | checkpointsTimedDesc = prometheus.NewDesc( 23 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_checkpoints_timed_total"), 24 | "Number of scheduled checkpoints that have been performed", 25 | nil, 26 | nil, 27 | ) 28 | 29 | checkpointsReqDesc = prometheus.NewDesc( 30 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_checkpoints_req_total"), 31 | "Number of requested checkpoints that have been performed", 32 | nil, 33 | nil, 34 | ) 35 | 36 | checkpointWriteTimeDesc = prometheus.NewDesc( 37 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_checkpoint_write_time_seconds_total"), 38 | "Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk", 39 | nil, 40 | nil, 41 | ) 42 | 43 | checkpointSyncTimeDesc = prometheus.NewDesc( 44 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_checkpoint_sync_time_seconds_total"), 45 | "Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk", 46 | nil, 47 | nil, 48 | ) 49 | 50 | buffersCheckpointDesc = prometheus.NewDesc( 51 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_buffers_checkpoint_total"), 52 | "Number of buffers written during checkpoints", 53 | nil, 54 | nil, 55 | ) 56 | 57 | buffersCleanDesc = prometheus.NewDesc( 58 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_buffers_clean_total"), 59 | "Number of buffers written by the background writer", 60 | nil, 61 | nil, 62 | ) 63 | 64 | maxWrittenCleanDesc = prometheus.NewDesc( 65 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_maxwritten_clean_total"), 66 | "Number of times the background writer stopped a cleaning scan because it had written too many buffers", 67 | nil, 68 | nil, 69 | ) 70 | 71 | buffersBackendDesc = prometheus.NewDesc( 72 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_buffers_backend_total"), 73 | "Number of buffers written directly by a backend", 74 | nil, 75 | nil, 76 | ) 77 | 78 | buffersBackendFsyncDesc = prometheus.NewDesc( 79 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_buffers_backend_fsync_total"), 80 | "Number of times a backend had to execute its own fsync call", 81 | nil, 82 | nil, 83 | ) 84 | 85 | buffersAllocDesc = prometheus.NewDesc( 86 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_buffers_alloc_total"), 87 | "Number of buffers allocated", 88 | nil, 89 | nil, 90 | ) 91 | 92 | statsResetDesc = prometheus.NewDesc( 93 | prometheus.BuildFQName(namespace, subSystemServer, "bgwriter_stats_reset_timestamp"), 94 | "Time at which these statistics were last reset", 95 | nil, 96 | nil, 97 | ) 98 | ) 99 | 100 | func NewBgWriterStateScraper() Scraper { 101 | return &bgWriterStateScraper{} 102 | } 103 | 104 | type bgWriterStateScraper struct{} 105 | 106 | func (bgWriterStateScraper) Name() string { 107 | return "bg_writer_state_scraper" 108 | } 109 | 110 | func (bgWriterStateScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 111 | querySql :=statBgwriterSql_V6; 112 | if ver < 6{ 113 | querySql=statBgwriterSql_V5; 114 | } 115 | 116 | rows, err := db.Query(querySql) 117 | logger.Infof("Query Database: %s", querySql) 118 | 119 | if err != nil { 120 | ch <- prometheus.MustNewConstMetric(stateDesc, prometheus.GaugeValue, 0, "", "") 121 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 122 | return err 123 | } 124 | 125 | defer rows.Close() 126 | 127 | for rows.Next() { 128 | var checkpointsTimedCounter, checkpointsReqCounter, 129 | buffersCheckpoint, buffersClean, maxWrittenClean, 130 | buffersBackend, buffersBackendFsync, buffersAlloc int64 131 | var checkpointWriteTime, checkpointSyncTime float64 132 | var statsReset time.Time 133 | 134 | err = rows.Scan(&checkpointsTimedCounter, 135 | &checkpointsReqCounter, 136 | &checkpointWriteTime, 137 | &checkpointSyncTime, 138 | &buffersCheckpoint, 139 | &buffersClean, 140 | &maxWrittenClean, 141 | &buffersBackend, 142 | &buffersBackendFsync, 143 | &buffersAlloc, 144 | &statsReset) 145 | if err != nil { 146 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 147 | return err 148 | } 149 | 150 | ch <- prometheus.MustNewConstMetric(checkpointsTimedDesc, prometheus.CounterValue, float64(checkpointsTimedCounter)) 151 | ch <- prometheus.MustNewConstMetric(checkpointsReqDesc, prometheus.CounterValue, float64(checkpointsReqCounter)) 152 | ch <- prometheus.MustNewConstMetric(checkpointWriteTimeDesc, prometheus.CounterValue, float64(checkpointWriteTime/1000)) 153 | ch <- prometheus.MustNewConstMetric(checkpointSyncTimeDesc, prometheus.CounterValue, float64(checkpointSyncTime/1000)) 154 | ch <- prometheus.MustNewConstMetric(buffersCheckpointDesc, prometheus.CounterValue, float64(buffersCheckpoint)) 155 | ch <- prometheus.MustNewConstMetric(buffersCleanDesc, prometheus.CounterValue, float64(buffersClean)) 156 | ch <- prometheus.MustNewConstMetric(maxWrittenCleanDesc, prometheus.CounterValue, float64(maxWrittenClean)) 157 | ch <- prometheus.MustNewConstMetric(buffersBackendDesc, prometheus.CounterValue, float64(buffersBackend)) 158 | ch <- prometheus.MustNewConstMetric(buffersBackendFsyncDesc, prometheus.CounterValue, float64(buffersBackendFsync)) 159 | ch <- prometheus.MustNewConstMetric(buffersAllocDesc, prometheus.CounterValue, float64(buffersAlloc)) 160 | ch <- prometheus.MustNewConstMetric(statsResetDesc, prometheus.GaugeValue, float64(statsReset.UTC().Unix())) 161 | 162 | return nil 163 | } 164 | 165 | return errors.New("bgwriter not found") 166 | } 167 | -------------------------------------------------------------------------------- /collector/cluster_state.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "time" 7 | 8 | "github.com/prometheus/client_golang/prometheus" 9 | logger "github.com/prometheus/common/log" 10 | ) 11 | 12 | /** 13 | * 集群状态抓取器 14 | */ 15 | 16 | const ( 17 | checkStateSql = `SELECT count(1) from gp_dist_random('gp_id')` 18 | versionSql = `select (select regexp_matches((select (select regexp_matches((select version()), 'Greenplum Database \d{1,}\.\d{1,}\.\d{1,}'))[1] as version), '\d{1,}\.\d{1,}\.\d{1,}'))[1];` 19 | masterNameSql = `SELECT hostname from gp_segment_configuration where content=-1 and role='p'` 20 | standbyNameSql = `SELECT hostname from gp_segment_configuration where content=-1 and role='m'` 21 | upTimeSql = `select extract(epoch from now() - pg_postmaster_start_time())` 22 | syncSql = `SELECT count(*) from pg_stat_replication where state='streaming'` 23 | configLoadTimeSql_V6 = `SELECT pg_conf_load_time() ` 24 | configLoadTimeSql_V5 = `select '2020-06-16 22:09:47.078+08'::timestamp as pg_conf_load_time; ` 25 | ) 26 | 27 | var ( 28 | stateDesc = prometheus.NewDesc( 29 | prometheus.BuildFQName(namespace, subSystemCluster, "state"), 30 | "Whether the GreenPlum database is accessible", 31 | []string{"version", "master", "standby"}, // variableLables 32 | nil, 33 | ) 34 | 35 | upTimeDesc = prometheus.NewDesc( 36 | prometheus.BuildFQName(namespace, subSystemCluster, "uptime"), 37 | "Duration that the GreenPlum database have been started since last up in second", 38 | nil, 39 | nil, 40 | ) 41 | 42 | syncDesc = prometheus.NewDesc( 43 | prometheus.BuildFQName(namespace, subSystemCluster, "sync"), 44 | "Whether the GreenPlum master node is synchronizing to standby", 45 | nil, 46 | nil, 47 | ) 48 | 49 | configLoadTimeDesc = prometheus.NewDesc( 50 | prometheus.BuildFQName(namespace, subSystemCluster, "config_last_load_time_seconds"), 51 | "Timestamp of the last configuration reload", 52 | nil, 53 | nil, 54 | ) 55 | ) 56 | 57 | func NewClusterStateScraper() Scraper { 58 | return &clusterStateScraper{} 59 | } 60 | 61 | type clusterStateScraper struct{} 62 | 63 | func (clusterStateScraper) Name() string { 64 | return "cluster_state_scraper" 65 | } 66 | 67 | func (clusterStateScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 68 | rows, err := db.Query(checkStateSql) 69 | logger.Infof("Query Database: %s", checkStateSql) 70 | 71 | if err != nil { 72 | ch <- prometheus.MustNewConstMetric(stateDesc, prometheus.GaugeValue, 0, "", "", "") 73 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 74 | return err 75 | } 76 | 77 | defer rows.Close() 78 | 79 | for rows.Next() { 80 | var count int 81 | err = rows.Scan(&count) 82 | if err != nil { 83 | ch <- prometheus.MustNewConstMetric(stateDesc, prometheus.GaugeValue, 0, "", "", "") 84 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 85 | return err 86 | } 87 | } 88 | 89 | version, errV := scrapeVersion(db) 90 | master, errM := scrapeMaster(db) 91 | standby, errX := scrapeStandby(db) 92 | upTime, errU := scrapeUpTime(db) 93 | sync, errW := scrapeSync(db) 94 | configLoadTime, errY := scrapeConfigLoadTime(db, ver) 95 | 96 | ch <- prometheus.MustNewConstMetric(stateDesc, prometheus.GaugeValue, 1, version, master, standby) 97 | ch <- prometheus.MustNewConstMetric(upTimeDesc, prometheus.GaugeValue, upTime) 98 | ch <- prometheus.MustNewConstMetric(syncDesc, prometheus.GaugeValue, sync) 99 | ch <- prometheus.MustNewConstMetric(configLoadTimeDesc, prometheus.GaugeValue, float64(configLoadTime.UTC().Unix())) 100 | 101 | return combineErr(errM, errV, errU, errW, errX, errY) 102 | } 103 | 104 | func scrapeUpTime(db *sql.DB) (upTime float64, err error) { 105 | rows, err := db.Query(upTimeSql) 106 | logger.Infof("Query Database Up Time: %s", upTimeSql) 107 | 108 | if err != nil { 109 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 110 | return 111 | } 112 | 113 | defer rows.Close() 114 | 115 | for rows.Next() { 116 | err = rows.Scan(&upTime) 117 | return 118 | } 119 | 120 | err = errors.New("start time of greenPlum not found") 121 | 122 | return 123 | } 124 | 125 | func scrapeVersion(db *sql.DB) (ver string, err error) { 126 | rows, err := db.Query(versionSql) 127 | logger.Infof("Query Database Version: %s", versionSql) 128 | 129 | if err != nil { 130 | return 131 | } 132 | 133 | defer rows.Close() 134 | 135 | for rows.Next() { 136 | err = rows.Scan(&ver) 137 | return 138 | } 139 | 140 | err = errors.New("greenPlum version not found") 141 | return 142 | } 143 | 144 | func scrapeMaster(db *sql.DB) (host string, err error) { 145 | rows, err := db.Query(masterNameSql) 146 | logger.Infof("Query Database Master Name: %s", masterNameSql) 147 | 148 | if err != nil { 149 | return 150 | } 151 | 152 | defer rows.Close() 153 | 154 | for rows.Next() { 155 | err = rows.Scan(&host) 156 | return 157 | } 158 | 159 | err = errors.New("hostname for master node not found") 160 | 161 | return 162 | } 163 | 164 | func scrapeStandby(db *sql.DB) (host string, err error) { 165 | rows, err := db.Query(standbyNameSql) 166 | logger.Infof("Query Database Standby Name: %s", standbyNameSql) 167 | 168 | if err != nil { 169 | return 170 | } 171 | 172 | defer rows.Close() 173 | 174 | for rows.Next() { 175 | err = rows.Scan(&host) 176 | return 177 | } 178 | 179 | return 180 | } 181 | 182 | func scrapeSync(db *sql.DB) (sync float64, err error) { 183 | rows, err := db.Query(syncSql) 184 | logger.Infof("Query Database Sync : %s", syncSql) 185 | 186 | if err != nil { 187 | return 188 | } 189 | 190 | defer rows.Close() 191 | 192 | for rows.Next() { 193 | err = rows.Scan(&sync) 194 | return 195 | } 196 | 197 | err = errors.New("greenPlum sync status not found") 198 | return 199 | } 200 | 201 | func scrapeConfigLoadTime(db *sql.DB, ver int) (time time.Time, err error) { 202 | querySql := configLoadTimeSql_V6 203 | if ver < 6 { 204 | querySql = configLoadTimeSql_V5 205 | } 206 | 207 | rows, err := db.Query(querySql) 208 | logger.Infof("Query Database Config load Time : %s", querySql) 209 | 210 | if err != nil { 211 | return 212 | } 213 | 214 | defer rows.Close() 215 | 216 | for rows.Next() { 217 | err = rows.Scan(&time) 218 | return 219 | } 220 | 221 | err = errors.New("greenPlum Config last load time not found") 222 | return 223 | } 224 | -------------------------------------------------------------------------------- /collector/collector.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | _ "github.com/lib/pq" 7 | "github.com/prometheus/client_golang/prometheus" 8 | "greenplum-exporter/stopwatch" 9 | logger "github.com/prometheus/common/log" 10 | "os" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | const verMajorSql=`select (select regexp_matches((select (select regexp_matches((select version()), 'Greenplum Database \d{1,}\.\d{1,}\.\d{1,}'))[1] as version), '\d{1,}'))[1];` 16 | 17 | // 定义采集器数据类型结构体 18 | type GreenPlumCollector struct { 19 | mu sync.Mutex 20 | 21 | db *sql.DB 22 | ver int 23 | metrics *ExporterMetrics 24 | scrapers []Scraper 25 | } 26 | 27 | /** 28 | * 函数:NewCollector 29 | * 功能:采集器的生成工厂方法 30 | */ 31 | func NewCollector(enabledScrapers []Scraper) *GreenPlumCollector { 32 | return &GreenPlumCollector{ 33 | metrics: NewMetrics(), 34 | scrapers: enabledScrapers, 35 | } 36 | } 37 | 38 | /** 39 | * 接口:Collect 40 | * 功能:抓取最新的数据,传递给channel 41 | */ 42 | func (c *GreenPlumCollector) Collect(ch chan<- prometheus.Metric) { 43 | c.mu.Lock() 44 | defer c.mu.Unlock() 45 | 46 | c.scrape(ch) 47 | 48 | ch <- c.metrics.totalScraped 49 | ch <- c.metrics.totalError 50 | ch <- c.metrics.scrapeDuration 51 | ch <- c.metrics.greenPlumUp 52 | } 53 | 54 | /** 55 | * 接口:Describe 56 | * 功能:传递结构体中的指标描述符到channel 57 | */ 58 | func (c *GreenPlumCollector) Describe(ch chan<- *prometheus.Desc) { 59 | ch <- c.metrics.greenPlumUp.Desc() 60 | ch <- c.metrics.scrapeDuration.Desc() 61 | ch <- c.metrics.totalScraped.Desc() 62 | ch <- c.metrics.totalError.Desc() 63 | } 64 | 65 | /** 66 | * 函数:scrape 67 | * 功能:执行实际的数据抓取 68 | */ 69 | func (c *GreenPlumCollector) scrape(ch chan<- prometheus.Metric) { 70 | start := time.Now() 71 | watch := stopwatch.New("scrape") 72 | 73 | // 检查并与Greenplum建立连接 74 | c.metrics.totalScraped.Inc() 75 | watch.MustStart("check connections") 76 | err := c.checkGreenPlumConn() 77 | watch.MustStop() 78 | if err != nil { 79 | c.metrics.totalError.Inc() 80 | c.metrics.scrapeDuration.Set(time.Since(start).Seconds()) 81 | c.metrics.greenPlumUp.Set(0) 82 | 83 | logger.Errorf("check database connection failed, error:%v", err) 84 | 85 | return 86 | } 87 | 88 | defer c.db.Close() 89 | 90 | logger.Info("check connections ok!") 91 | c.metrics.greenPlumUp.Set(1) 92 | 93 | // 遍历执行MAP中的所有抓取器 94 | for _, scraper := range c.scrapers { 95 | logger.Info("#### scraping start : " + scraper.Name()) 96 | watch.MustStart("scraping: " + scraper.Name()) 97 | err := scraper.Scrape(c.db, ch, c.ver) 98 | watch.MustStop() 99 | if err != nil { 100 | logger.Errorf("get metrics for scraper:%s failed, error:%v", scraper.Name(), err.Error()) 101 | } 102 | logger.Info("#### scraping end : " + scraper.Name()) 103 | } 104 | 105 | c.metrics.scrapeDuration.Set(time.Since(start).Seconds()) 106 | 107 | logger.Info(fmt.Sprintf("prometheus scraped grennplum exporter successfully at %v, detail elapsed:%s", time.Now(), watch.PrettyPrint())) 108 | } 109 | 110 | /** 111 | * 函数:checkGreenPlumConn 112 | * 功能:检查Greenplum数据库的连接 113 | */ 114 | func (c *GreenPlumCollector) checkGreenPlumConn() (err error) { 115 | if c.db == nil { 116 | return c.getGreenPlumConnection() 117 | } 118 | 119 | if err = c.getGreenplumMajorVersion(c.db); err == nil { 120 | return nil 121 | } else { 122 | _ = c.db.Close() 123 | c.db = nil 124 | return c.getGreenPlumConnection() 125 | } 126 | } 127 | 128 | /** 129 | * 函数:getGreenPlumConnection 130 | * 功能:获取Greenplum数据库的连接 131 | */ 132 | func (c *GreenPlumCollector) getGreenPlumConnection() error { 133 | //使用PostgreSQL的驱动连接数据库,可参考如下教程: 134 | //参考:https://blog.csdn.net/u010412301/article/details/85037685 135 | dataSourceName := os.Getenv("GPDB_DATA_SOURCE_URL") 136 | 137 | db, err := sql.Open("postgres", dataSourceName) 138 | 139 | if err != nil { 140 | return err 141 | } 142 | 143 | if err = c.getGreenplumMajorVersion(db); err != nil { 144 | _ = db.Close() 145 | return err 146 | } 147 | 148 | db.SetMaxIdleConns(1) 149 | db.SetMaxOpenConns(1) 150 | 151 | c.db = db 152 | 153 | return nil 154 | } 155 | 156 | /** 157 | * 函数:getGreenplumMajorVersion 158 | * 功能:获取Greenplum数据库的主版本号 159 | */ 160 | func (c *GreenPlumCollector) getGreenplumMajorVersion(db *sql.DB) error { 161 | err := db.Ping() 162 | 163 | if err != nil { 164 | return err 165 | } 166 | 167 | rows, err := db.Query(verMajorSql) 168 | 169 | if err != nil { 170 | return err 171 | } 172 | 173 | for rows.Next() { 174 | var verMajor int 175 | errC := rows.Scan(&verMajor) 176 | if errC != nil { 177 | return errC 178 | } 179 | 180 | c.ver=verMajor 181 | } 182 | 183 | defer rows.Close() 184 | 185 | return nil 186 | } 187 | -------------------------------------------------------------------------------- /collector/connections.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | 7 | "github.com/prometheus/client_golang/prometheus" 8 | logger "github.com/prometheus/common/log" 9 | ) 10 | 11 | /** 12 | * 连接数量抓取器 13 | */ 14 | 15 | const ( 16 | connectionsSql_V6 = `select 17 | count(*) total, 18 | count(*) filter(where state<>'active') idle, 19 | count(*) filter(where state='active') active, 20 | count(*) filter(where state='active' and not waiting) running, 21 | count(*) filter(where state='active' and waiting) waiting 22 | from pg_stat_activity where pid <> pg_backend_pid();` 23 | connectionsSql_V5 = `select 24 | count(*) total, 25 | count(*) filter(where current_query='') idle, 26 | count(*) filter(where current_query<>'') active, 27 | count(*) filter(where current_query<>'' and not waiting) running, 28 | count(*) filter(where current_query<>'' and waiting) waiting 29 | from pg_stat_activity where procpid <> pg_backend_pid();` 30 | ) 31 | 32 | var ( 33 | currentConnDesc = prometheus.NewDesc( 34 | prometheus.BuildFQName(namespace, subSystemCluster, "total_connections"), 35 | "Current connections of GreenPlum cluster at scrape time", 36 | nil, nil, 37 | ) 38 | 39 | idleConnDesc = prometheus.NewDesc( 40 | prometheus.BuildFQName(namespace, subSystemCluster, "idle_connections"), 41 | "Idle connections of GreenPlum cluster at scape time", 42 | nil, nil, 43 | ) 44 | 45 | activeConnDesc = prometheus.NewDesc( 46 | prometheus.BuildFQName(namespace, subSystemCluster, "active_connections"), 47 | "Active connections of GreenPlum cluster at scape time", 48 | nil, nil, 49 | ) 50 | 51 | runningConnDesc = prometheus.NewDesc( 52 | prometheus.BuildFQName(namespace, subSystemCluster, "running_connections"), 53 | "Running sql count of GreenPlum cluster at scape time", 54 | nil, nil, 55 | ) 56 | 57 | queuingConnDesc = prometheus.NewDesc( 58 | prometheus.BuildFQName(namespace, subSystemCluster, "waiting_connections"), 59 | "Waiting sql count of GreenPlum cluster at scape time", 60 | nil, nil, 61 | ) 62 | ) 63 | 64 | func NewConnectionsScraper() Scraper { 65 | return &connectionsScraper{} 66 | } 67 | 68 | type connectionsScraper struct{} 69 | 70 | func (connectionsScraper) Name() string { 71 | return "connections_scraper" 72 | } 73 | 74 | func (connectionsScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 75 | querySql := connectionsSql_V6 76 | if ver < 6 { 77 | querySql = connectionsSql_V5 78 | } 79 | 80 | rows, err := db.Query(querySql) 81 | logger.Infof("Query Database: %s", querySql) 82 | 83 | if err != nil { 84 | return err 85 | } 86 | 87 | defer rows.Close() 88 | 89 | for rows.Next() { 90 | var total, idle, active, running, waiting float64 91 | 92 | err = rows.Scan(&total, &idle, &active, &running, &waiting) 93 | 94 | if err != nil { 95 | return err 96 | } 97 | 98 | ch <- prometheus.MustNewConstMetric(currentConnDesc, prometheus.GaugeValue, total) 99 | ch <- prometheus.MustNewConstMetric(idleConnDesc, prometheus.GaugeValue, idle) 100 | ch <- prometheus.MustNewConstMetric(activeConnDesc, prometheus.GaugeValue, active) 101 | ch <- prometheus.MustNewConstMetric(runningConnDesc, prometheus.GaugeValue, running) 102 | ch <- prometheus.MustNewConstMetric(queuingConnDesc, prometheus.GaugeValue, waiting) 103 | 104 | return nil 105 | } 106 | 107 | return errors.New("connections not found") 108 | } 109 | -------------------------------------------------------------------------------- /collector/connections_detail.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/prometheus/client_golang/prometheus" 7 | logger "github.com/prometheus/common/log" 8 | ) 9 | 10 | /** 11 | * 连接详情抓取器 12 | */ 13 | 14 | const ( 15 | connectionsByUserSql_V6 = `select usename, 16 | count(*) total, 17 | count(*) filter(where state<>'active') idle, 18 | count(*) filter(where state='active') active 19 | from pg_stat_activity group by 1;` 20 | connectionsByUserSql_V5 = `select usename, 21 | count(*) total, 22 | count(*) filter(where current_query='') idle, 23 | count(*) filter(where current_query<>'') active 24 | from pg_stat_activity group by 1;` 25 | connectionsByClientAddressSql_V6 = `select client_addr, 26 | count(*) total, 27 | count(*) filter(where state<>'active') idle, 28 | count(*) filter(where state='active') active 29 | from pg_stat_activity where pid <> pg_backend_pid() group by 1;` 30 | connectionsByClientAddressSql_V5 = `select client_addr, 31 | count(*) total, 32 | count(*) filter(where current_query='') idle, 33 | count(*) filter(where current_query<>'') active 34 | from pg_stat_activity where procpid <> pg_backend_pid() group by 1;` 35 | ) 36 | 37 | var ( 38 | totalPerUserDesc = prometheus.NewDesc( 39 | prometheus.BuildFQName(namespace, subSystemCluster, "total_connections_per_user"), 40 | "Total connections of specified database user", 41 | []string{"usename"}, nil, 42 | ) 43 | 44 | activePerUserDesc = prometheus.NewDesc( 45 | prometheus.BuildFQName(namespace, subSystemCluster, "active_connections_per_user"), 46 | "Active connections of specified database user", 47 | []string{"usename"}, nil, 48 | ) 49 | 50 | idlePerUserDesc = prometheus.NewDesc( 51 | prometheus.BuildFQName(namespace, subSystemCluster, "idle_connections_per_user"), 52 | "Idle connections of specified database user", 53 | []string{"usename"}, nil, 54 | ) 55 | 56 | totalPerClientDesc = prometheus.NewDesc( 57 | prometheus.BuildFQName(namespace, subSystemCluster, "total_connections_per_client"), 58 | "Total connections of specified database user", 59 | []string{"client"}, nil, 60 | ) 61 | 62 | activePerClientDesc = prometheus.NewDesc( 63 | prometheus.BuildFQName(namespace, subSystemCluster, "active_connections_per_client"), 64 | "Active connections of specified database user", 65 | []string{"client"}, nil, 66 | ) 67 | 68 | idlePerClientDesc = prometheus.NewDesc( 69 | prometheus.BuildFQName(namespace, subSystemCluster, "idle_connections_per_client"), 70 | "Idle connections of specified database user", 71 | []string{"client"}, nil, 72 | ) 73 | 74 | totalCountClientDesc = prometheus.NewDesc( 75 | prometheus.BuildFQName(namespace, subSystemCluster, "total_client_count"), 76 | "The total client count of greenplum database", 77 | nil, nil, 78 | ) 79 | 80 | totalCountOnlineUsersDesc = prometheus.NewDesc( 81 | prometheus.BuildFQName(namespace, subSystemCluster, "total_online_user_count"), 82 | "The total online user count of greenplum database", 83 | nil, nil, 84 | ) 85 | ) 86 | 87 | func NewConnDetailScraper() Scraper { 88 | return connectionsDetailScraper{} 89 | } 90 | 91 | type connectionsDetailScraper struct{} 92 | 93 | func (connectionsDetailScraper) Name() string { 94 | return "connections_detail_scraper" 95 | } 96 | 97 | func (connectionsDetailScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 98 | errU := scrapeLoadByUser(db, ch, ver) 99 | errC := scrapeLoadByClient(db, ch, ver) 100 | 101 | return combineErr(errC, errU) 102 | } 103 | 104 | func scrapeLoadByUser(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 105 | querySql := connectionsByUserSql_V6 106 | if ver < 6 { 107 | querySql = connectionsByUserSql_V5 108 | } 109 | 110 | rows, err := db.Query(querySql) 111 | 112 | logger.Infof("Query Database: %s", querySql) 113 | 114 | if err != nil { 115 | return err 116 | } 117 | 118 | defer rows.Close() 119 | 120 | errs := make([]error, 0) 121 | 122 | var totalOnlineUserCount int = 0 123 | for rows.Next() { 124 | var usename string 125 | var total, idle, active float64 126 | 127 | err = rows.Scan(&usename, &total, &idle, &active) 128 | 129 | if err != nil { 130 | errs = append(errs, err) 131 | continue 132 | } 133 | 134 | ch <- prometheus.MustNewConstMetric(totalPerUserDesc, prometheus.GaugeValue, total, usename) 135 | ch <- prometheus.MustNewConstMetric(idlePerUserDesc, prometheus.GaugeValue, idle, usename) 136 | ch <- prometheus.MustNewConstMetric(activePerUserDesc, prometheus.GaugeValue, active, usename) 137 | 138 | totalOnlineUserCount++ 139 | } 140 | 141 | ch <- prometheus.MustNewConstMetric(totalCountOnlineUsersDesc, prometheus.GaugeValue, float64(totalOnlineUserCount)) 142 | 143 | return combineErr(errs...) 144 | } 145 | 146 | func scrapeLoadByClient(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 147 | querySql := connectionsByClientAddressSql_V6 148 | if ver < 6 { 149 | querySql = connectionsByClientAddressSql_V5 150 | } 151 | 152 | rows, err := db.Query(querySql) 153 | 154 | if err != nil { 155 | return err 156 | } 157 | 158 | defer rows.Close() 159 | 160 | errs := make([]error, 0) 161 | 162 | var totalClientCount int = 0 163 | for rows.Next() { 164 | var client sql.NullString 165 | var total, idle, active float64 166 | 167 | err = rows.Scan(&client, &total, &idle, &active) 168 | 169 | if err != nil { 170 | errs = append(errs, err) 171 | } 172 | 173 | ch <- prometheus.MustNewConstMetric(totalPerClientDesc, prometheus.GaugeValue, total, client.String) 174 | ch <- prometheus.MustNewConstMetric(idlePerClientDesc, prometheus.GaugeValue, idle, client.String) 175 | ch <- prometheus.MustNewConstMetric(activePerClientDesc, prometheus.GaugeValue, active, client.String) 176 | 177 | totalClientCount++ 178 | } 179 | 180 | ch <- prometheus.MustNewConstMetric(totalCountClientDesc, prometheus.GaugeValue, float64(totalClientCount)) 181 | 182 | return combineErr(errs...) 183 | } 184 | -------------------------------------------------------------------------------- /collector/database_size.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "container/list" 5 | "context" 6 | "database/sql" 7 | "os" 8 | "strings" 9 | "time" 10 | 11 | "github.com/prometheus/client_golang/prometheus" 12 | logger "github.com/prometheus/common/log" 13 | ) 14 | 15 | /** 16 | * 各个数据库存储大小、数据膨胀列表、数据倾斜列表、缓存命中率、事务提交率等 17 | */ 18 | 19 | const ( 20 | databaseSizeSql = `SELECT sodddatname as database_name,sodddatsize/(1024*1024) as database_size_mb from gp_toolkit.gp_size_of_database;` 21 | tableCountSql = `SELECT count(*) as total from information_schema.tables where table_schema not in ('gp_toolkit','information_schema','pg_catalog');` 22 | bloatTableSql = ` 23 | SELECT current_database(),bdinspname,bdirelname,bdirelpages,bdiexppages,( 24 | case 25 | when position('moderate' in bdidiag)>0 then 1 26 | when position('significant' in bdidiag)>0 then 2 27 | else 0 28 | end) as bloat_state 29 | FROM gp_toolkit.gp_bloat_diag ORDER BY bloat_state desc 30 | ` 31 | skewTableSql = ` 32 | SELECT current_database(),schema_name,table_name,max_div_avg,pg_size_pretty(total_size) table_size 33 | FROM ( 34 | SELECT schema_name,table_name, 35 | MAX(size)/(AVG(size)+0.001) AS max_div_avg, 36 | CAST(SUM(size) AS BIGINT) total_size 37 | FROM 38 | ( 39 | SELECT o.gp_segment_id, 40 | n.nspname as schema_name, 41 | o.relname as table_name, 42 | pg_relation_size(o.oid) size 43 | FROM gp_dist_random('pg_class') o 44 | LEFT JOIN pg_namespace n on o.relnamespace=n.oid 45 | WHERE o.relkind='r' 46 | AND o.relstorage IN ('a','h') 47 | ) t 48 | GROUP BY schema_name,table_name 49 | )tab 50 | WHERE total_size >= 1024*1024*1024 51 | AND max_div_avg>1.5 52 | ORDER BY total_size DESC; 53 | ` 54 | hitCacheRateSql = `select sum(blks_hit)/(sum(blks_read)+sum(blks_hit))*100 from pg_stat_database;` 55 | txCommitRateSql = `select sum(xact_commit)/(sum(xact_commit)+sum(xact_rollback))*100 from pg_stat_database;` 56 | ) 57 | 58 | var ( 59 | databaseSizeDesc = prometheus.NewDesc( 60 | prometheus.BuildFQName(namespace, subSystemNode, "database_name_mb_size"), //指标的名称 61 | "Total MB size of each database name in the file system", //帮助信息,显示在指标的上面作为注释 62 | []string{"dbname"}, //定义的label名称数组 63 | nil, //定义的Labels 64 | ) 65 | 66 | tablesCountDesc = prometheus.NewDesc( 67 | prometheus.BuildFQName(namespace, subSystemNode, "database_table_total_count"), 68 | "Total table count of each database name in the file system", 69 | []string{"dbname"}, 70 | nil, 71 | ) 72 | 73 | bloatTableDesc = prometheus.NewDesc( 74 | prometheus.BuildFQName(namespace, subSystemServer, "database_table_bloat_list"), 75 | "Bloat table list of each database name in greenplum cluster", 76 | []string{"dbname", "schema", "table", "relpages", "exppages"}, 77 | nil, 78 | ) 79 | 80 | skewTableDesc = prometheus.NewDesc( 81 | prometheus.BuildFQName(namespace, subSystemServer, "database_table_skew_list"), 82 | "Skew table list of each database name in greenplum cluster", 83 | []string{"dbname", "schema", "table", "size"}, 84 | nil, 85 | ) 86 | 87 | hitCacheRateDesc = prometheus.NewDesc( 88 | prometheus.BuildFQName(namespace, subSystemServer, "database_hit_cache_percent_rate"), 89 | "Cache hit percent rat for all of database in greenplum server system", 90 | nil, 91 | nil, 92 | ) 93 | 94 | txCommitRateDesc = prometheus.NewDesc( 95 | prometheus.BuildFQName(namespace, subSystemServer, "database_transition_commit_percent_rate"), 96 | "Transition commit percent rat for all of database in greenplum server system", 97 | nil, 98 | nil, 99 | ) 100 | ) 101 | 102 | func NewDatabaseSizeScraper() Scraper { 103 | return databaseSizeScraper{} 104 | } 105 | 106 | type databaseSizeScraper struct{} 107 | 108 | func (databaseSizeScraper) Name() string { 109 | return "database_size_scraper" 110 | } 111 | 112 | func (databaseSizeScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 113 | ctx := context.Background() 114 | ctx, cancel := context.WithTimeout(ctx, time.Second*10) 115 | 116 | defer cancel() 117 | 118 | logger.Infof("Query Database: %s", databaseSizeSql) 119 | rows, err := db.QueryContext(ctx, databaseSizeSql) 120 | if err != nil { 121 | return err 122 | } 123 | 124 | defer rows.Close() 125 | 126 | errs := make([]error, 0) 127 | 128 | names := list.New() 129 | for rows.Next() { 130 | var dbname string 131 | var mbSize float64 132 | 133 | err := rows.Scan(&dbname, &mbSize) 134 | 135 | if err != nil { 136 | errs = append(errs, err) 137 | continue 138 | } 139 | 140 | ch <- prometheus.MustNewConstMetric(databaseSizeDesc, prometheus.GaugeValue, mbSize, dbname) 141 | names.PushBack(dbname) 142 | } 143 | 144 | for item := names.Front(); nil != item; item = item.Next() { 145 | dbname := item.Value.(string) 146 | count, err := queryTablesCount(dbname, ch) 147 | if err != nil { 148 | errs = append(errs, err) 149 | continue 150 | } 151 | 152 | ch <- prometheus.MustNewConstMetric(tablesCountDesc, prometheus.GaugeValue, count, dbname) 153 | } 154 | 155 | errM := queryHitCacheRate(db, ch) 156 | if errM != nil { 157 | errs = append(errs, errM) 158 | } 159 | 160 | errN := queryTxCommitRate(db, ch) 161 | if errN != nil { 162 | errs = append(errs, errN) 163 | } 164 | 165 | return combineErr(errs...) 166 | } 167 | 168 | func queryTablesCount(dbname string, ch chan<- prometheus.Metric) (count float64, err error) { 169 | dataSourceName := os.Getenv("GPDB_DATA_SOURCE_URL") 170 | newDataSourceName := strings.Replace(dataSourceName, "/postgres", "/"+dbname, 1) 171 | logger.Infof("Connection string is : %s", newDataSourceName) 172 | conn, errA := sql.Open("postgres", newDataSourceName) 173 | 174 | if errA != nil { 175 | err = errA 176 | return 177 | } 178 | 179 | defer conn.Close() 180 | 181 | rows, errB := conn.Query(tableCountSql) 182 | logger.Infof("Query Database: %s", tableCountSql) 183 | 184 | if errB != nil { 185 | err = errB 186 | return 187 | } 188 | 189 | defer rows.Close() 190 | 191 | for rows.Next() { 192 | errC := rows.Scan(&count) 193 | if errC != nil { 194 | err = errC 195 | return 196 | } 197 | } 198 | 199 | // errD := queryBloatTables(conn, ch) 200 | // if errD != nil { 201 | // err=errD 202 | // return 203 | // } 204 | 205 | // errF := querySkewTables(conn, ch) 206 | // if errF != nil { 207 | // err = errF 208 | // return 209 | // } 210 | 211 | return 212 | } 213 | 214 | func queryBloatTables(conn *sql.DB, ch chan<- prometheus.Metric) error { 215 | rows, err := conn.Query(bloatTableSql) 216 | logger.Infof("Query bloat tables sql: %s", bloatTableSql) 217 | 218 | if err != nil { 219 | return err 220 | } 221 | 222 | defer rows.Close() 223 | 224 | errs := make([]error, 0) 225 | 226 | for rows.Next() { 227 | var dbname, schema, table, relpages, exppages string 228 | var bloatstate float64 229 | err = rows.Scan(&dbname, &schema, &table, &relpages, &exppages, &bloatstate) 230 | if err != nil { 231 | errs = append(errs, err) 232 | continue 233 | } 234 | 235 | ch <- prometheus.MustNewConstMetric(bloatTableDesc, prometheus.GaugeValue, bloatstate, dbname, schema, table, relpages, exppages) 236 | } 237 | 238 | return combineErr(errs...) 239 | } 240 | 241 | func querySkewTables(conn *sql.DB, ch chan<- prometheus.Metric) error { 242 | rows, err := conn.Query(skewTableSql) 243 | logger.Infof("Query skew tables sql: %s", skewTableSql) 244 | 245 | if err != nil { 246 | return err 247 | } 248 | 249 | defer rows.Close() 250 | 251 | errs := make([]error, 0) 252 | 253 | for rows.Next() { 254 | var dbname, schema, table, size string 255 | var slope float64 256 | err = rows.Scan(&dbname, &schema, &table, &slope, &size) 257 | if err != nil { 258 | errs = append(errs, err) 259 | continue 260 | } 261 | 262 | ch <- prometheus.MustNewConstMetric(skewTableDesc, prometheus.GaugeValue, slope, dbname, schema, table, size) 263 | } 264 | 265 | return combineErr(errs...) 266 | } 267 | 268 | func queryHitCacheRate(db *sql.DB, ch chan<- prometheus.Metric) error { 269 | rows, err := db.Query(hitCacheRateSql) 270 | logger.Infof("Query Database: %s", hitCacheRateSql) 271 | 272 | if err != nil { 273 | return err 274 | } 275 | 276 | defer rows.Close() 277 | 278 | for rows.Next() { 279 | var rate float64 280 | err = rows.Scan(&rate) 281 | 282 | ch <- prometheus.MustNewConstMetric(hitCacheRateDesc, prometheus.GaugeValue, rate) 283 | 284 | break 285 | } 286 | 287 | return nil 288 | } 289 | 290 | func queryTxCommitRate(db *sql.DB, ch chan<- prometheus.Metric) error { 291 | rows, err := db.Query(txCommitRateSql) 292 | logger.Infof("Query Database: %s", txCommitRateSql) 293 | 294 | if err != nil { 295 | return err 296 | } 297 | 298 | defer rows.Close() 299 | 300 | for rows.Next() { 301 | var rate float64 302 | err = rows.Scan(&rate) 303 | 304 | ch <- prometheus.MustNewConstMetric(txCommitRateDesc, prometheus.GaugeValue, rate) 305 | 306 | break 307 | } 308 | 309 | return nil 310 | } 311 | -------------------------------------------------------------------------------- /collector/diskspace.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/prometheus/client_golang/prometheus" 6 | logger "github.com/prometheus/common/log" 7 | ) 8 | 9 | /** 10 | * 存储磁盘抓取器 11 | */ 12 | 13 | const ( 14 | //????????????????????????????????????? 15 | fileSystemSql = `select * from diskspace_now;` 16 | ) 17 | 18 | var ( 19 | fsTotalDesc = prometheus.NewDesc( 20 | prometheus.BuildFQName(namespace, subSystemNode, "fs_total_bytes"), 21 | "Total bytes in the file system", 22 | []string{"hostname", "filesystem"}, nil, 23 | ) 24 | 25 | fsUsedDesc = prometheus.NewDesc( 26 | prometheus.BuildFQName(namespace, subSystemNode, "fs_used_bytes"), 27 | "Total bytes used in the file system", 28 | []string{"hostname", "filesystem"}, nil, 29 | ) 30 | 31 | fsAvailableDesc = prometheus.NewDesc( 32 | prometheus.BuildFQName(namespace, subSystemNode, "fs_available_bytes"), 33 | "Total bytes available in the file system", 34 | []string{"hostname", "filesystem"}, nil, 35 | ) 36 | ) 37 | 38 | func NewDiskScraper() Scraper { 39 | return diskScraper{} 40 | } 41 | 42 | type diskScraper struct{} 43 | 44 | func (diskScraper) Name() string { 45 | return "filesystem_scraper" 46 | } 47 | 48 | func (diskScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 49 | rows, err := db.Query(fileSystemSql) 50 | logger.Infof("Query Database: %s",fileSystemSql) 51 | 52 | if err != nil { 53 | return err 54 | } 55 | 56 | defer rows.Close() 57 | 58 | errs := make([]error, 0) 59 | 60 | for rows.Next() { 61 | var cTime, hostname, fs string 62 | var total, used, available float64 63 | 64 | err := rows.Scan(&cTime, &hostname, &fs, &total, &used, &available) 65 | 66 | if err != nil { 67 | errs = append(errs, err) 68 | continue 69 | } 70 | 71 | ch <- prometheus.MustNewConstMetric(fsTotalDesc, prometheus.GaugeValue, total, hostname, fs) 72 | ch <- prometheus.MustNewConstMetric(fsUsedDesc, prometheus.GaugeValue, used, hostname, fs) 73 | ch <- prometheus.MustNewConstMetric(fsAvailableDesc, prometheus.GaugeValue, available, hostname, fs) 74 | } 75 | 76 | return combineErr(errs...) 77 | } 78 | -------------------------------------------------------------------------------- /collector/dynamic_memory.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/prometheus/client_golang/prometheus" 6 | logger "github.com/prometheus/common/log" 7 | ) 8 | 9 | /** 10 | * 实时动态内存抓取器 11 | */ 12 | 13 | const ( 14 | //????????????????????????????????????? 15 | dynamicMemorySql = `select hostname, dynamic_memory_used_mb, dynamic_memory_available_mb from memory_info order by 1 DESC limit 1;` 16 | ) 17 | 18 | var ( 19 | dynamicMemUsedDesc = prometheus.NewDesc( 20 | prometheus.BuildFQName(namespace, subSystemNode, "dynamic_memory_used_mb"), 21 | "The amount of dynamic memory in MB allocated to query processes running on this segment host", 22 | []string{"hostname"}, nil, 23 | ) 24 | 25 | dynamicMemAvailableDesc = prometheus.NewDesc( 26 | prometheus.BuildFQName(namespace, subSystemNode, "dynamic_memory_available_mb"), 27 | "The amount of additional dynamic memory (in MB) available to the query processes running on this segment host", 28 | []string{"hostname"}, nil, 29 | ) 30 | ) 31 | 32 | func NewDynamicMemoryScraper() Scraper { 33 | return &dynamicMemoryScraper{} 34 | } 35 | 36 | type dynamicMemoryScraper struct{} 37 | 38 | func (dynamicMemoryScraper) Name() string { 39 | return "dynamic_mem_scraper" 40 | } 41 | 42 | func (dynamicMemoryScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 43 | rows, err := db.Query(dynamicMemorySql) 44 | logger.Infof("Query Database: %s",dynamicMemorySql) 45 | 46 | if err != nil { 47 | return err 48 | } 49 | 50 | defer rows.Close() 51 | 52 | errs := make([]error, 0) 53 | for rows.Next() { 54 | var hostname string 55 | var used, available float64 56 | err = rows.Scan(&hostname, &used, &available) 57 | 58 | if err != nil { 59 | errs = append(errs, err) 60 | continue 61 | } 62 | 63 | ch <- prometheus.MustNewConstMetric(dynamicMemUsedDesc, prometheus.GaugeValue, used, hostname) 64 | ch <- prometheus.MustNewConstMetric(dynamicMemAvailableDesc, prometheus.GaugeValue, available, hostname) 65 | } 66 | 67 | return combineErr(errs...) 68 | } 69 | -------------------------------------------------------------------------------- /collector/error.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import "errors" 4 | 5 | /** 6 | * 函数:combineErr 7 | * 功能:error的组合 8 | */ 9 | func combineErr(errs ...error) error { 10 | var errStr string 11 | for _, err := range errs { 12 | if err != nil { 13 | if errStr == "" { 14 | errStr += err.Error() 15 | } else { 16 | errStr += "; " + err.Error() 17 | } 18 | 19 | } 20 | } 21 | 22 | if errStr == "" { 23 | return nil 24 | } else { 25 | return errors.New(errStr) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /collector/exporter_metrics.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | const ( 6 | namespace = "greenplum" 7 | subSystemServer = "server" 8 | subsystemExporter = "exporter" 9 | subSystemCluster = "cluster" 10 | subSystemNode = "node" 11 | ) 12 | 13 | // 定义指标类型结构体 14 | type ExporterMetrics struct { 15 | totalScraped prometheus.Counter 16 | totalError prometheus.Counter 17 | scrapeDuration prometheus.Gauge 18 | greenPlumUp prometheus.Gauge 19 | } 20 | 21 | /** 22 | * 函数:NewMetrics 23 | * 功能:指标的生成工厂方法 24 | */ 25 | func NewMetrics() *ExporterMetrics { 26 | return &ExporterMetrics{ 27 | totalScraped: prometheus.NewCounter( 28 | prometheus.CounterOpts{ 29 | Namespace: namespace, 30 | Subsystem: subsystemExporter, 31 | Name: "total_scraped", 32 | Help: "Total scraped", 33 | }, 34 | ), 35 | totalError: prometheus.NewCounter( 36 | prometheus.CounterOpts{ 37 | Namespace: namespace, 38 | Subsystem: subsystemExporter, 39 | Name: "total_error", 40 | Help: "Total error scraping", 41 | }, 42 | ), 43 | scrapeDuration: prometheus.NewGauge( 44 | prometheus.GaugeOpts{ 45 | Namespace: namespace, 46 | Subsystem: subsystemExporter, 47 | Name: "scrape_duration_second", 48 | Help: "Elapsed of each scrape", 49 | }, 50 | ), 51 | greenPlumUp: prometheus.NewGauge( 52 | prometheus.GaugeOpts{ 53 | Namespace: namespace, 54 | Name: "up", 55 | Help: "Whether greenPlum cluster is reachable", 56 | }, 57 | ), 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /collector/locks.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "time" 6 | 7 | "github.com/prometheus/client_golang/prometheus" 8 | logger "github.com/prometheus/common/log" 9 | ) 10 | 11 | /** 12 | * 数据库锁信息抓取器 13 | */ 14 | 15 | const ( 16 | locksQuerySql_V6 = ` 17 | SELECT pg_locks.pid 18 | , pg_database.datname 19 | , pg_stat_activity.usename 20 | , locktype 21 | , mode 22 | , pg_stat_activity.application_name 23 | , state 24 | , CASE 25 | WHEN granted='f' THEN 26 | 'wait_lock' 27 | WHEN granted='t' THEN 28 | 'get_lock' 29 | END lock_satus 30 | , pg_stat_activity.query 31 | , least(query_start,xact_start) start_time 32 | , count(*)::float 33 | FROM pg_locks 34 | JOIN pg_database ON pg_locks.database=pg_database.oid 35 | JOIN pg_stat_activity on pg_locks.pid=pg_stat_activity.pid 36 | WHERE NOT pg_locks.pid=pg_backend_pid() 37 | AND pg_stat_activity.application_name<>'pg_statsinfod' 38 | GROUP BY pg_locks.pid, pg_database.datname,pg_stat_activity.usename, locktype, mode, 39 | pg_stat_activity.application_name, state , lock_satus ,pg_stat_activity.query, start_time 40 | ORDER BY start_time 41 | ` 42 | locksQuerySql_V5 = ` 43 | SELECT pg_locks.pid 44 | , pg_database.datname 45 | , pg_stat_activity.usename 46 | , locktype 47 | , mode 48 | , pg_stat_activity.application_name 49 | , 'unkown' as state 50 | , CASE 51 | WHEN granted='f' THEN 52 | 'wait_lock' 53 | WHEN granted='t' THEN 54 | 'get_lock' 55 | END lock_satus 56 | , pg_stat_activity.current_query 57 | , least(query_start,xact_start) start_time 58 | , count(*)::float 59 | FROM pg_locks 60 | JOIN pg_database ON pg_locks.database=pg_database.oid 61 | JOIN pg_stat_activity on pg_locks.pid=pg_stat_activity.procpid 62 | WHERE NOT pg_locks.pid=pg_backend_pid() 63 | AND pg_stat_activity.application_name<>'pg_statsinfod' 64 | GROUP BY pg_locks.pid, pg_database.datname,pg_stat_activity.usename, locktype, mode, 65 | pg_stat_activity.application_name, state , lock_satus ,pg_stat_activity.current_query, start_time 66 | ORDER BY start_time 67 | ` 68 | ) 69 | 70 | var ( 71 | locksDesc = prometheus.NewDesc( 72 | prometheus.BuildFQName(namespace, subSystemServer, "locks_table_detail"), 73 | "Table locks detail for greenplum database", 74 | []string{"pid", "datname", "usename", "locktype", "mode", "application_name", "state", "lock_satus", "query"}, 75 | nil, 76 | ) 77 | ) 78 | 79 | func NewLocksScraper() Scraper { 80 | return &locksScraper{} 81 | } 82 | 83 | type locksScraper struct{} 84 | 85 | func (locksScraper) Name() string { 86 | return "locks_scraper" 87 | } 88 | 89 | func (locksScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 90 | querySql := locksQuerySql_V6 91 | if ver < 6 { 92 | querySql = locksQuerySql_V5 93 | } 94 | 95 | rows, err := db.Query(querySql) 96 | logger.Infof("Query Database: %s", querySql) 97 | 98 | if err != nil { 99 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 100 | return err 101 | } 102 | 103 | defer rows.Close() 104 | 105 | for rows.Next() { 106 | var pid, datname, usename, locktype, mode, application_name, state, lock_satus, query string 107 | var startTime time.Time 108 | var count int64 109 | 110 | err = rows.Scan(&pid, 111 | &datname, 112 | &usename, 113 | &locktype, 114 | &mode, 115 | &application_name, 116 | &state, 117 | &lock_satus, 118 | &query, 119 | &startTime, 120 | &count) 121 | if err != nil { 122 | logger.Errorf("get metrics for scraper, error:%v", err.Error()) 123 | return err 124 | } 125 | 126 | ch <- prometheus.MustNewConstMetric(locksDesc, prometheus.GaugeValue, float64(startTime.UTC().Unix()), pid, datname, usename, locktype, mode, application_name, state, lock_satus, query) 127 | } 128 | 129 | return nil 130 | } 131 | -------------------------------------------------------------------------------- /collector/max_connections.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "fmt" 7 | "github.com/prometheus/client_golang/prometheus" 8 | logger "github.com/prometheus/common/log" 9 | ) 10 | 11 | /** 12 | * 最大连接抓取器 13 | */ 14 | 15 | const ( 16 | maxConnectionsSql = `show max_connections` 17 | suReservedSql = `show superuser_reserved_connections` 18 | ) 19 | 20 | var ( 21 | maxConnDesc = prometheus.NewDesc( 22 | prometheus.BuildFQName(namespace, subSystemCluster, "max_connections"), 23 | "Max connection of greenPlum cluster", 24 | nil, nil, 25 | ) 26 | ) 27 | 28 | func NewMaxConnScraper() Scraper { 29 | return maxConnScraper{} 30 | } 31 | 32 | type maxConnScraper struct{} 33 | 34 | func (maxConnScraper) Name() string { 35 | return "max_connection_scraper" 36 | } 37 | 38 | func (maxConnScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 39 | maxConn, err := showConnections(db, maxConnectionsSql) 40 | 41 | if err != nil { 42 | return err 43 | } 44 | 45 | reserved, err := showConnections(db, suReservedSql) 46 | 47 | if err != nil { 48 | logger.Warn(err.Error()) 49 | } 50 | 51 | //这里的最大连接数应为max_connections减去superuser_reserved_connections 52 | ch <- prometheus.MustNewConstMetric(maxConnDesc, prometheus.GaugeValue, maxConn-reserved) 53 | 54 | return nil 55 | } 56 | 57 | func showConnections(db *sql.DB, sql string) (conn float64, err error) { 58 | rows, err := db.Query(sql) 59 | logger.Infof("Query Database: %s", sql) 60 | 61 | if err != nil { 62 | return 63 | } 64 | 65 | defer rows.Close() 66 | 67 | for rows.Next() { 68 | err = rows.Scan(&conn) 69 | 70 | return 71 | } 72 | 73 | err = errors.New(fmt.Sprintf("%s not found", sql)) 74 | return 75 | } 76 | -------------------------------------------------------------------------------- /collector/queries.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "github.com/prometheus/client_golang/prometheus" 7 | logger "github.com/prometheus/common/log" 8 | ) 9 | 10 | /** 11 | * SQL查询信息抓取器 12 | */ 13 | 14 | const ( 15 | //????????????????????????????????????? 16 | queriesSql = `select * from database_now;` 17 | ) 18 | 19 | var ( 20 | totalQueriesDesc = prometheus.NewDesc( 21 | prometheus.BuildFQName(namespace, subSystemCluster, "total_queries"), 22 | "The total number of queries in Greenplum Database at data collection time", 23 | nil, nil, 24 | ) 25 | 26 | runningQueriesDesc = prometheus.NewDesc( 27 | prometheus.BuildFQName(namespace, subSystemCluster, "running_queries"), 28 | "The number of active queries running at data collection time", 29 | nil, nil, 30 | ) 31 | 32 | queuedQueriesDesc = prometheus.NewDesc( 33 | prometheus.BuildFQName(namespace, subSystemCluster, "queued_queries"), 34 | "The number of queries waiting in a resource group or resource queue", 35 | nil, nil, 36 | ) 37 | ) 38 | 39 | func NewQueryScraper() Scraper { 40 | return queriesScraper{} 41 | } 42 | 43 | type queriesScraper struct{} 44 | 45 | func (queriesScraper) Name() string { 46 | return "queriesScraper" 47 | } 48 | 49 | func (queriesScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 50 | rows, err := db.Query(queriesSql) 51 | logger.Infof("Query Database: %s",queriesSql) 52 | 53 | if err != nil { 54 | return err 55 | } 56 | 57 | defer rows.Close() 58 | 59 | for rows.Next() { 60 | var cTime string 61 | var total, running, queued sql.NullFloat64 62 | err = rows.Scan(&cTime, &total, &running, &queued) 63 | 64 | if err != nil { 65 | return err 66 | } 67 | 68 | ch <- prometheus.MustNewConstMetric(totalQueriesDesc, prometheus.GaugeValue, total.Float64) 69 | ch <- prometheus.MustNewConstMetric(runningQueriesDesc, prometheus.GaugeValue, running.Float64) 70 | ch <- prometheus.MustNewConstMetric(queuedQueriesDesc, prometheus.GaugeValue, queued.Float64) 71 | 72 | return nil 73 | } 74 | 75 | return errors.New("queries info not found") 76 | } 77 | -------------------------------------------------------------------------------- /collector/scrapers.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/prometheus/client_golang/prometheus" 6 | ) 7 | 8 | // 抓取器Scraper接口定义 9 | // 实现包括: 10 | // clusterStateScraper、connectionsScraper、connectionsDetailScraper、diskScraper、 11 | // dynamicMemoryScraper、maxConnScraper、queriesScraper、segmentScraper、systemScraper 12 | type Scraper interface { 13 | 14 | // Scraper的名称. 需要唯一. 15 | Name() string 16 | 17 | // 从数据库连接中获取数据信息,并发送到数据类型为prometheus metric的通道里. 18 | Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error 19 | } 20 | -------------------------------------------------------------------------------- /collector/segment.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "time" 7 | 8 | "github.com/prometheus/client_golang/prometheus" 9 | logger "github.com/prometheus/common/log" 10 | ) 11 | 12 | /** 13 | * Segment的抓取器 14 | * 抓取参数包括:节点状态status、最优角色运转preferred_role、正在重新同步mode、磁盘剩余空间disk_free等参数 15 | */ 16 | 17 | const ( 18 | segmentConfigSql_V6 = `select dbid,content,role,preferred_role,mode,status,port,hostname,address,datadir from gp_segment_configuration;` 19 | segmentConfigSql_V5 = `select dbid,content,role,preferred_role,mode,status,port,hostname,address,null as datadir from gp_segment_configuration;` 20 | 21 | segmentDiskFreeSizeSql = `SELECT dfhostname as segment_hostname,min(dfspace)/(1024*1024) as segment_disk_free_gb from gp_toolkit.gp_disk_free GROUP BY dfhostname;` 22 | ) 23 | 24 | var ( 25 | statusDesc = prometheus.NewDesc( 26 | prometheus.BuildFQName(namespace, subSystemNode, "segment_status"), 27 | "UP(1) if the segment is running, DOWN(0) if the segment has failed or is unreachable", 28 | []string{"hostname", "address", "dbid", "content", "preferred_role", "port", "data_dir"}, nil, 29 | ) 30 | 31 | roleDesc = prometheus.NewDesc( 32 | prometheus.BuildFQName(namespace, subSystemNode, "segment_role"), 33 | "The segment's current role, either primary or mirror", 34 | []string{"hostname", "address", "dbid", "content", "preferred_role", "port", "data_dir"}, nil, 35 | ) 36 | 37 | modeDesc = prometheus.NewDesc( 38 | prometheus.BuildFQName(namespace, subSystemNode, "segment_mode"), 39 | "The replication status for the segment", 40 | []string{"hostname", "address", "dbid", "content", "preferred_role", "port", "data_dir"}, nil, 41 | ) 42 | 43 | segmentDiskFreeSizeDesc = prometheus.NewDesc( 44 | prometheus.BuildFQName(namespace, subSystemNode, "segment_disk_free_gb_size"), //指标的名称 45 | "Total GB size of each segment node free size of disk in the file system", //帮助信息,显示在指标的上面作为注释 46 | []string{"hostname"}, //定义的label名称数组 47 | nil, //定义的Labels 48 | ) 49 | ) 50 | 51 | func NewSegmentScraper() Scraper { 52 | return segmentScraper{} 53 | } 54 | 55 | type segmentScraper struct{} 56 | 57 | func (segmentScraper) Name() string { 58 | return "segment_scraper" 59 | } 60 | 61 | func (segmentScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 62 | errU := scrapeSegmentConfig(db, ch, ver) 63 | errC := scrapeSegmentDiskFree(db, ch) 64 | 65 | return combineErr(errC, errU) 66 | } 67 | 68 | func scrapeSegmentConfig(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 69 | ctx := context.Background() 70 | ctx, cancel := context.WithTimeout(ctx, time.Second*2) 71 | 72 | defer cancel() 73 | 74 | querySql := segmentConfigSql_V6 75 | if ver < 6 { 76 | querySql = segmentConfigSql_V5 77 | } 78 | 79 | logger.Infof("Query Database: %s", querySql) 80 | rows, err := db.QueryContext(ctx, querySql) 81 | 82 | if err != nil { 83 | return err 84 | } 85 | 86 | defer rows.Close() 87 | 88 | errs := make([]error, 0) 89 | 90 | for rows.Next() { 91 | var dbID, content, role, preferredRole, mode, status, hostname, address, port string 92 | var rp sql.NullString 93 | 94 | err = rows.Scan(&dbID, &content, &role, &preferredRole, &mode, &status, &port, &hostname, &address, &rp) 95 | 96 | if err != nil { 97 | errs = append(errs, err) 98 | continue 99 | } 100 | 101 | ch <- prometheus.MustNewConstMetric(statusDesc, prometheus.GaugeValue, getStatus(status), hostname, address, dbID, content, preferredRole, port, rp.String) 102 | ch <- prometheus.MustNewConstMetric(roleDesc, prometheus.GaugeValue, getRole(role), hostname, address, dbID, content, preferredRole, port, rp.String) 103 | ch <- prometheus.MustNewConstMetric(modeDesc, prometheus.GaugeValue, getMode(mode), hostname, address, dbID, content, preferredRole, port, rp.String) 104 | } 105 | 106 | return combineErr(errs...) 107 | } 108 | 109 | func scrapeSegmentDiskFree(db *sql.DB, ch chan<- prometheus.Metric) error { 110 | ctx := context.Background() 111 | ctx, cancel := context.WithTimeout(ctx, time.Second*10) 112 | 113 | defer cancel() 114 | 115 | logger.Infof("Query Database: %s", segmentDiskFreeSizeSql) 116 | rows, err := db.QueryContext(ctx, segmentDiskFreeSizeSql) 117 | 118 | if err != nil { 119 | return err 120 | } 121 | 122 | defer rows.Close() 123 | 124 | errs := make([]error, 0) 125 | 126 | for rows.Next() { 127 | var hostName string 128 | var mbSize float64 129 | 130 | err := rows.Scan(&hostName, &mbSize) 131 | 132 | if err != nil { 133 | errs = append(errs, err) 134 | continue 135 | } 136 | 137 | ch <- prometheus.MustNewConstMetric(segmentDiskFreeSizeDesc, prometheus.GaugeValue, mbSize, hostName) 138 | } 139 | 140 | return combineErr(errs...) 141 | } 142 | -------------------------------------------------------------------------------- /collector/segment_utils.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import "strings" 4 | 5 | // 转换参考:http://gpdb.docs.pivotal.io/530/ref_guide/system_catalogs/gp_segment_configuration.html 6 | var ( 7 | sgStatus = map[string]float64{"u": 1, "d": 0} //1-UP; 0-DOWN 8 | sgMode = map[string]float64{"s": 1, "r": 2, "c": 3, "n": 4} // 1-synchronized ; 2-resyncing; 3-change logging; 4-not synchronized 9 | sgRole = map[string]float64{"p": 1, "m": 2} //1-Primary ; 2-Mirror 10 | ) 11 | 12 | func getRole(role string) float64 { 13 | lowerR := strings.ToLower(role) 14 | 15 | if rf, ok := sgRole[lowerR]; ok { 16 | return rf 17 | } 18 | 19 | return 2 20 | } 21 | 22 | func getMode(mode string) float64 { 23 | lowerM := strings.ToLower(mode) 24 | 25 | if mf, ok := sgMode[lowerM]; ok { 26 | return mf 27 | } 28 | 29 | return 4 30 | } 31 | 32 | func getStatus(status string) float64 { 33 | lowerS := strings.ToLower(status) 34 | 35 | if sf, ok := sgStatus[lowerS]; ok { 36 | return sf 37 | } 38 | 39 | return 0 40 | } 41 | -------------------------------------------------------------------------------- /collector/system.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/prometheus/client_golang/prometheus" 6 | logger "github.com/prometheus/common/log" 7 | ) 8 | 9 | /** 10 | * 系统信息抓取器 11 | */ 12 | 13 | const ( 14 | //????????????????????????????????????? 15 | systemMetricsSql = `select * from system_now;` 16 | ) 17 | 18 | var ( 19 | memTotalDesc = prometheus.NewDesc( 20 | prometheus.BuildFQName(namespace, subSystemNode, "mem_total_bytes"), 21 | "Segment or master hostname associated with these system metrics", 22 | []string{"hostname"}, nil, 23 | ) 24 | 25 | memUsedDesc = prometheus.NewDesc( 26 | prometheus.BuildFQName(namespace, subSystemNode, "mem_used_bytes"), 27 | "Total system memory in Bytes for this host", 28 | []string{"hostname"}, nil, 29 | ) 30 | 31 | memActualUsedDesc = prometheus.NewDesc( 32 | prometheus.BuildFQName(namespace, subSystemNode, "mem_actual_used_bytes"), 33 | "Used actual memory in Bytes for this host", 34 | []string{"hostname"}, nil, 35 | ) 36 | 37 | memActualFreeDesc = prometheus.NewDesc( 38 | prometheus.BuildFQName(namespace, subSystemNode, "mem_actual_free_bytes"), 39 | "Free actual memory in Bytes for this host", 40 | []string{"hostname"}, nil, 41 | ) 42 | 43 | swapTotalDesc = prometheus.NewDesc( 44 | prometheus.BuildFQName(namespace, subSystemNode, "swap_total_bytes"), 45 | "Total swap space in Bytes for this host", 46 | []string{"hostname"}, nil, 47 | ) 48 | 49 | swapUsedDesc = prometheus.NewDesc( 50 | prometheus.BuildFQName(namespace, subSystemNode, "swap_used_bytes"), 51 | "Used swap space in Bytes for this host", 52 | []string{"hostname"}, nil, 53 | ) 54 | 55 | swapPageInDesc = prometheus.NewDesc( 56 | prometheus.BuildFQName(namespace, subSystemNode, "swap_page_in"), 57 | "Number of swap pages in", 58 | []string{"hostname"}, nil, 59 | ) 60 | 61 | swapPageOutDesc = prometheus.NewDesc( 62 | prometheus.BuildFQName(namespace, subSystemNode, "swap_page_out"), 63 | "Number of swap pages out", 64 | []string{"hostname"}, nil, 65 | ) 66 | 67 | cpuUserDesc = prometheus.NewDesc( 68 | prometheus.BuildFQName(namespace, subSystemNode, "cpu_user_percent"), 69 | "CPU usage by the Greenplum system user", 70 | []string{"hostname"}, nil, 71 | ) 72 | 73 | cpuSysDesc = prometheus.NewDesc( 74 | prometheus.BuildFQName(namespace, subSystemNode, "cpu_sys_percent"), 75 | "CPU usage for this host", 76 | []string{"hostname"}, nil, 77 | ) 78 | 79 | cpuIdleDesc = prometheus.NewDesc( 80 | prometheus.BuildFQName(namespace, subSystemNode, "cpu_idle_percent"), 81 | "Idle CPU capacity at metric collection time", 82 | []string{"hostname"}, nil, 83 | ) 84 | 85 | cpuAvg1mDesc = prometheus.NewDesc( 86 | prometheus.BuildFQName(namespace, subSystemNode, "cpu_avg_usage_1m_percent"), 87 | "CPU load average for the prior one-minute period", 88 | []string{"hostname"}, nil, 89 | ) 90 | 91 | cpuAvg5mDesc = prometheus.NewDesc( 92 | prometheus.BuildFQName(namespace, subSystemNode, "cpu_avg_usage_5m_percent"), 93 | "CPU load average for the prior five-minutes period", 94 | []string{"hostname"}, nil, 95 | ) 96 | 97 | cpuAvg15mDesc = prometheus.NewDesc( 98 | prometheus.BuildFQName(namespace, subSystemNode, "cpu_avg_usage_15m_percent"), 99 | "CPU load average for the prior fifteen-minutes period", 100 | []string{"hostname"}, nil, 101 | ) 102 | 103 | diskRoDesc = prometheus.NewDesc( 104 | prometheus.BuildFQName(namespace, subSystemNode, "disk_ro_rate"), 105 | "Disk read operations per second", 106 | []string{"hostname"}, nil, 107 | ) 108 | 109 | diskWoDesc = prometheus.NewDesc( 110 | prometheus.BuildFQName(namespace, subSystemNode, "disk_wo_rate"), 111 | "Disk write operations per second", 112 | []string{"hostname"}, nil, 113 | ) 114 | 115 | diskRbDesc = prometheus.NewDesc( 116 | prometheus.BuildFQName(namespace, subSystemNode, "disk_rb_rate"), 117 | "Bytes per second for disk read operations", 118 | []string{"hostname"}, nil, 119 | ) 120 | 121 | diskWbDesc = prometheus.NewDesc( 122 | prometheus.BuildFQName(namespace, subSystemNode, "disk_wb_rate"), 123 | "Bytes per second for disk write operations", 124 | []string{"hostname"}, nil, 125 | ) 126 | 127 | netRpDesc = prometheus.NewDesc( 128 | prometheus.BuildFQName(namespace, subSystemNode, "net_rp_rate"), 129 | "Packets per second on the system network for read operations", 130 | []string{"hostname"}, nil, 131 | ) 132 | 133 | netWpDesc = prometheus.NewDesc( 134 | prometheus.BuildFQName(namespace, subSystemNode, "net_wp_rate"), 135 | "Packets per second on the system network for write operations", 136 | []string{"hostname"}, nil, 137 | ) 138 | 139 | netRbDesc = prometheus.NewDesc( 140 | prometheus.BuildFQName(namespace, subSystemNode, "net_rb_rate"), 141 | "Bytes per second on the system network for read operations", 142 | []string{"hostname"}, nil, 143 | ) 144 | 145 | netWbDesc = prometheus.NewDesc( 146 | prometheus.BuildFQName(namespace, subSystemNode, "net_wb_rate"), 147 | "Bytes per second on the system network for write operations", 148 | []string{"hostname"}, nil, 149 | ) 150 | ) 151 | 152 | func NewSystemScraper() Scraper { 153 | return systemScraper{} 154 | } 155 | 156 | type systemScraper struct{} 157 | 158 | func (systemScraper) Name() string { 159 | return "systemScraper" 160 | } 161 | 162 | func (systemScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 163 | rows, err := db.Query(systemMetricsSql) 164 | logger.Infof("Query Database: %s",systemMetricsSql) 165 | 166 | if err != nil { 167 | return err 168 | } 169 | 170 | defer rows.Close() 171 | 172 | for rows.Next() { 173 | var cTime, hostname string 174 | var memTotal, memUsed, memActualUsed, memActualFree, swapTotal, swapUsed, swapPageIn, swapPageOut, 175 | cpuUser, cpuSys, cpuIdle, load1m, load5m, load15m, quantum, diskRo, diskWo, diskRb, diskWb, netRp, netWp, netRb, netWb float64 176 | 177 | err = rows.Scan(&cTime, &hostname, &memTotal, &memUsed, &memActualUsed, &memActualFree, &swapTotal, 178 | &swapUsed, &swapPageIn, &swapPageOut, &cpuUser, &cpuSys, &cpuIdle, &load1m, &load5m, &load15m, 179 | &quantum, &diskRo, &diskWo, &diskRb, &diskWb, &netRp, &netWp, &netRb, &netWb) 180 | 181 | if err != nil { 182 | return err 183 | } 184 | 185 | ch <- prometheus.MustNewConstMetric(memTotalDesc, prometheus.GaugeValue, memTotal, hostname) 186 | ch <- prometheus.MustNewConstMetric(memUsedDesc, prometheus.GaugeValue, memUsed, hostname) 187 | ch <- prometheus.MustNewConstMetric(memActualUsedDesc, prometheus.GaugeValue, memActualUsed, hostname) 188 | ch <- prometheus.MustNewConstMetric(memActualFreeDesc, prometheus.GaugeValue, memActualFree, hostname) 189 | ch <- prometheus.MustNewConstMetric(swapTotalDesc, prometheus.GaugeValue, swapTotal, hostname) 190 | ch <- prometheus.MustNewConstMetric(swapUsedDesc, prometheus.GaugeValue, swapUsed, hostname) 191 | ch <- prometheus.MustNewConstMetric(swapPageInDesc, prometheus.GaugeValue, swapPageIn, hostname) 192 | ch <- prometheus.MustNewConstMetric(swapPageOutDesc, prometheus.GaugeValue, swapPageOut, hostname) 193 | ch <- prometheus.MustNewConstMetric(cpuUserDesc, prometheus.GaugeValue, cpuUser, hostname) 194 | ch <- prometheus.MustNewConstMetric(cpuSysDesc, prometheus.GaugeValue, cpuSys, hostname) 195 | ch <- prometheus.MustNewConstMetric(cpuIdleDesc, prometheus.GaugeValue, cpuIdle, hostname) 196 | ch <- prometheus.MustNewConstMetric(cpuAvg1mDesc, prometheus.GaugeValue, load1m, hostname) 197 | ch <- prometheus.MustNewConstMetric(cpuAvg5mDesc, prometheus.GaugeValue, load5m, hostname) 198 | ch <- prometheus.MustNewConstMetric(cpuAvg15mDesc, prometheus.GaugeValue, load15m, hostname) 199 | ch <- prometheus.MustNewConstMetric(diskRoDesc, prometheus.GaugeValue, diskRo, hostname) 200 | ch <- prometheus.MustNewConstMetric(diskWoDesc, prometheus.GaugeValue, diskWo, hostname) 201 | ch <- prometheus.MustNewConstMetric(diskRbDesc, prometheus.GaugeValue, diskRb, hostname) 202 | ch <- prometheus.MustNewConstMetric(diskWbDesc, prometheus.GaugeValue, diskWb, hostname) 203 | ch <- prometheus.MustNewConstMetric(netRpDesc, prometheus.GaugeValue, netRp, hostname) 204 | ch <- prometheus.MustNewConstMetric(netWpDesc, prometheus.GaugeValue, netWp, hostname) 205 | ch <- prometheus.MustNewConstMetric(netRbDesc, prometheus.GaugeValue, netRb, hostname) 206 | ch <- prometheus.MustNewConstMetric(netWbDesc, prometheus.GaugeValue, netWb, hostname) 207 | } 208 | 209 | return nil 210 | } 211 | -------------------------------------------------------------------------------- /collector/users.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/prometheus/client_golang/prometheus" 6 | logger "github.com/prometheus/common/log" 7 | ) 8 | 9 | /** 10 | * 用户信息抓取器 11 | */ 12 | 13 | const ( 14 | usersSql = `SELECT usename from pg_catalog.pg_user;` 15 | ) 16 | 17 | var ( 18 | usersCountDesc = prometheus.NewDesc( 19 | prometheus.BuildFQName(namespace, subSystemServer, "users_total_count"), 20 | "Total user account number for current greenplum database", 21 | nil, 22 | nil, 23 | ) 24 | 25 | usersNameDesc = prometheus.NewDesc( 26 | prometheus.BuildFQName(namespace, subSystemServer, "users_name_list"), 27 | "Each user account name for current greenplum database", 28 | []string{"username"}, 29 | nil, 30 | ) 31 | ) 32 | 33 | func NewUsersScraper() Scraper { 34 | return usersScraper{} 35 | } 36 | 37 | type usersScraper struct{} 38 | 39 | func (usersScraper) Name() string { 40 | return "users_scraper" 41 | } 42 | 43 | func (usersScraper) Scrape(db *sql.DB, ch chan<- prometheus.Metric, ver int) error { 44 | rows, err := db.Query(usersSql) 45 | logger.Infof("Query Database: %s", usersSql) 46 | 47 | if err != nil { 48 | return err 49 | } 50 | 51 | defer rows.Close() 52 | 53 | errs := make([]error, 0) 54 | 55 | count := 1 56 | for rows.Next() { 57 | var username string 58 | 59 | err := rows.Scan(&username) 60 | 61 | if err != nil { 62 | errs = append(errs, err) 63 | continue 64 | } 65 | 66 | count++ 67 | ch <- prometheus.MustNewConstMetric(usersNameDesc, prometheus.GaugeValue, 1, username) 68 | } 69 | 70 | ch <- prometheus.MustNewConstMetric(usersCountDesc, prometheus.GaugeValue, float64(count)) 71 | 72 | return combineErr(errs...) 73 | } 74 | -------------------------------------------------------------------------------- /doc/DEMO.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangyibo/greenplum_exporter/5eb2898cbf00b22087b0e338971e235c804841d5/doc/DEMO.PNG -------------------------------------------------------------------------------- /docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf ./bin/ 4 | docker run -it --rm \ 5 | --name my-golang-project \ 6 | -v "$PWD":/home \ 7 | inrgihc/centos7-golang:1.14.12 /bin/sh -c 'mkdir -p ./bin && go mod download && go build -o ./bin/greenplum_exporter' 8 | 9 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module greenplum-exporter 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/lib/pq v1.7.1 7 | github.com/prometheus/client_golang v1.7.1 8 | github.com/prometheus/common v0.10.0 9 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 2 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= 3 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 4 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 5 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= 6 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 12 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 15 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 16 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 17 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 18 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 19 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 20 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 21 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 22 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 23 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 24 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 25 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 26 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 27 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 28 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 29 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 30 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 31 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 32 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 33 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 34 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 35 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 36 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 37 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 38 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 39 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 40 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 41 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 42 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 43 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 44 | github.com/lib/pq v1.7.1 h1:FvD5XTVTDt+KON6oIoOmHq6B6HzGuYEhuTMpEG0yuBQ= 45 | github.com/lib/pq v1.7.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 46 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 47 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 48 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 49 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 50 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 51 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 52 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 53 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 54 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 55 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 56 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 57 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 58 | github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= 59 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 60 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 61 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 62 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 63 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 64 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 65 | github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= 66 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 67 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 68 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 69 | github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= 70 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 71 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 72 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 73 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 74 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 75 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 76 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 77 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 78 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 79 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 80 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 81 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 82 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 83 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 84 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 85 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 86 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 87 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 88 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 89 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 90 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 91 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= 92 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 93 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 94 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 95 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 96 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 97 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 98 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 99 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 100 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 101 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 102 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 103 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 104 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 105 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 106 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 107 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 108 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 109 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 110 | -------------------------------------------------------------------------------- /grafana/greenplum_dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_PROMETHEUS", 5 | "label": "Prometheus", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "prometheus", 9 | "pluginName": "Prometheus" 10 | } 11 | ], 12 | "__requires": [ 13 | { 14 | "type": "grafana", 15 | "id": "grafana", 16 | "name": "Grafana", 17 | "version": "6.4.2" 18 | }, 19 | { 20 | "type": "panel", 21 | "id": "graph", 22 | "name": "Graph", 23 | "version": "" 24 | }, 25 | { 26 | "type": "panel", 27 | "id": "heatmap", 28 | "name": "Heatmap", 29 | "version": "" 30 | }, 31 | { 32 | "type": "datasource", 33 | "id": "prometheus", 34 | "name": "Prometheus", 35 | "version": "1.0.0" 36 | }, 37 | { 38 | "type": "panel", 39 | "id": "text", 40 | "name": "Text", 41 | "version": "" 42 | } 43 | ], 44 | "annotations": { 45 | "list": [ 46 | { 47 | "builtIn": 1, 48 | "datasource": "-- Grafana --", 49 | "enable": true, 50 | "hide": true, 51 | "iconColor": "rgba(0, 211, 255, 1)", 52 | "name": "Annotations & Alerts", 53 | "type": "dashboard" 54 | } 55 | ] 56 | }, 57 | "editable": true, 58 | "gnetId": 13822, 59 | "graphTooltip": 0, 60 | "id": 1, 61 | "iteration": 1609222287631, 62 | "links": [], 63 | "panels": [ 64 | { 65 | "datasource": "${DS_PROMETHEUS}", 66 | "description": "Greenplum截至到当前的运行总时长", 67 | "fieldConfig": { 68 | "defaults": { 69 | "custom": {}, 70 | "decimals": 2, 71 | "mappings": [], 72 | "thresholds": { 73 | "mode": "percentage", 74 | "steps": [ 75 | { 76 | "color": "green", 77 | "value": null 78 | }, 79 | { 80 | "color": "red", 81 | "value": 80 82 | } 83 | ] 84 | }, 85 | "unit": "s" 86 | }, 87 | "overrides": [] 88 | }, 89 | "gridPos": { 90 | "h": 4, 91 | "w": 3, 92 | "x": 0, 93 | "y": 0 94 | }, 95 | "id": 8, 96 | "options": { 97 | "orientation": "auto", 98 | "reduceOptions": { 99 | "calcs": [ 100 | "last" 101 | ], 102 | "fields": "", 103 | "values": false 104 | }, 105 | "showThresholdLabels": false, 106 | "showThresholdMarkers": true 107 | }, 108 | "pluginVersion": "7.0.5", 109 | "repeat": null, 110 | "targets": [ 111 | { 112 | "expr": "greenplum_cluster_uptime", 113 | "interval": "", 114 | "legendFormat": "运行时间", 115 | "refId": "C" 116 | } 117 | ], 118 | "timeFrom": null, 119 | "timeShift": null, 120 | "title": "运行时间", 121 | "type": "gauge" 122 | }, 123 | { 124 | "content": "
$version
\n\n\n", 125 | "datasource": "${DS_PROMETHEUS}", 126 | "description": "Greenplum的版本号", 127 | "fieldConfig": { 128 | "defaults": { 129 | "custom": {}, 130 | "mappings": [], 131 | "thresholds": { 132 | "mode": "absolute", 133 | "steps": [ 134 | { 135 | "color": "green", 136 | "value": null 137 | }, 138 | { 139 | "color": "red", 140 | "value": 80 141 | } 142 | ] 143 | } 144 | }, 145 | "overrides": [] 146 | }, 147 | "gridPos": { 148 | "h": 4, 149 | "w": 2, 150 | "x": 3, 151 | "y": 0 152 | }, 153 | "id": 18, 154 | "mode": "html", 155 | "pluginVersion": "7.0.5", 156 | "targets": [ 157 | { 158 | "expr": "greenplum_cluster_state", 159 | "interval": "", 160 | "legendFormat": "", 161 | "refId": "A" 162 | } 163 | ], 164 | "timeFrom": null, 165 | "timeShift": null, 166 | "title": "版本号", 167 | "type": "text" 168 | }, 169 | { 170 | "datasource": "${DS_PROMETHEUS}", 171 | "description": "Greenplum数据库的总数据量", 172 | "fieldConfig": { 173 | "defaults": { 174 | "custom": {}, 175 | "decimals": 2, 176 | "mappings": [], 177 | "thresholds": { 178 | "mode": "absolute", 179 | "steps": [ 180 | { 181 | "color": "green", 182 | "value": null 183 | } 184 | ] 185 | }, 186 | "unit": "decmbytes" 187 | }, 188 | "overrides": [] 189 | }, 190 | "gridPos": { 191 | "h": 4, 192 | "w": 3, 193 | "x": 5, 194 | "y": 0 195 | }, 196 | "id": 52, 197 | "options": { 198 | "colorMode": "value", 199 | "graphMode": "none", 200 | "justifyMode": "center", 201 | "orientation": "auto", 202 | "reduceOptions": { 203 | "calcs": [ 204 | "lastNotNull" 205 | ], 206 | "fields": "/^Value$/", 207 | "values": false 208 | } 209 | }, 210 | "pluginVersion": "7.0.5", 211 | "targets": [ 212 | { 213 | "expr": "sum(greenplum_node_database_name_mb_size)", 214 | "instant": true, 215 | "interval": "", 216 | "legendFormat": "", 217 | "refId": "A" 218 | } 219 | ], 220 | "timeFrom": null, 221 | "timeShift": null, 222 | "title": "集群数据总量", 223 | "transparent": true, 224 | "type": "stat" 225 | }, 226 | { 227 | "datasource": "${DS_PROMETHEUS}", 228 | "description": "Greenplum集群的Master节点是否可连通", 229 | "fieldConfig": { 230 | "defaults": { 231 | "custom": {}, 232 | "mappings": [ 233 | { 234 | "from": "", 235 | "id": 0, 236 | "operator": "", 237 | "text": "是", 238 | "to": "", 239 | "type": 1, 240 | "value": "1" 241 | }, 242 | { 243 | "from": "", 244 | "id": 1, 245 | "operator": "", 246 | "text": "否", 247 | "to": "", 248 | "type": 1, 249 | "value": "0" 250 | } 251 | ], 252 | "thresholds": { 253 | "mode": "absolute", 254 | "steps": [ 255 | { 256 | "color": "green", 257 | "value": null 258 | }, 259 | { 260 | "color": "red", 261 | "value": 80 262 | } 263 | ] 264 | } 265 | }, 266 | "overrides": [] 267 | }, 268 | "gridPos": { 269 | "h": 4, 270 | "w": 2, 271 | "x": 8, 272 | "y": 0 273 | }, 274 | "id": 38, 275 | "options": { 276 | "orientation": "auto", 277 | "reduceOptions": { 278 | "calcs": [ 279 | "last" 280 | ], 281 | "fields": "", 282 | "values": false 283 | }, 284 | "showThresholdLabels": false, 285 | "showThresholdMarkers": true 286 | }, 287 | "pluginVersion": "7.0.5", 288 | "targets": [ 289 | { 290 | "expr": "greenplum_up", 291 | "interval": "", 292 | "legendFormat": "", 293 | "refId": "A" 294 | } 295 | ], 296 | "timeFrom": null, 297 | "timeShift": null, 298 | "title": "Master可达", 299 | "type": "gauge" 300 | }, 301 | { 302 | "datasource": "${DS_PROMETHEUS}", 303 | "description": "Greenplum集群的Standby节点是否正在保持与Master的同步", 304 | "fieldConfig": { 305 | "defaults": { 306 | "custom": {}, 307 | "mappings": [ 308 | { 309 | "from": "", 310 | "id": 0, 311 | "operator": "", 312 | "text": "是", 313 | "to": "", 314 | "type": 1, 315 | "value": "1" 316 | }, 317 | { 318 | "from": "", 319 | "id": 1, 320 | "operator": "", 321 | "text": "否", 322 | "to": "", 323 | "type": 1, 324 | "value": "0" 325 | } 326 | ], 327 | "thresholds": { 328 | "mode": "absolute", 329 | "steps": [ 330 | { 331 | "color": "green", 332 | "value": null 333 | }, 334 | { 335 | "color": "red", 336 | "value": 80 337 | } 338 | ] 339 | } 340 | }, 341 | "overrides": [] 342 | }, 343 | "gridPos": { 344 | "h": 4, 345 | "w": 2, 346 | "x": 10, 347 | "y": 0 348 | }, 349 | "id": 40, 350 | "options": { 351 | "orientation": "auto", 352 | "reduceOptions": { 353 | "calcs": [ 354 | "last" 355 | ], 356 | "fields": "", 357 | "values": false 358 | }, 359 | "showThresholdLabels": false, 360 | "showThresholdMarkers": true 361 | }, 362 | "pluginVersion": "7.0.5", 363 | "targets": [ 364 | { 365 | "expr": "greenplum_cluster_sync", 366 | "interval": "", 367 | "legendFormat": "", 368 | "refId": "A" 369 | } 370 | ], 371 | "timeFrom": null, 372 | "timeShift": null, 373 | "title": "Standby同步", 374 | "type": "gauge" 375 | }, 376 | { 377 | "datasource": "${DS_PROMETHEUS}", 378 | "description": "当前连接总数", 379 | "fieldConfig": { 380 | "defaults": { 381 | "custom": {}, 382 | "mappings": [], 383 | "thresholds": { 384 | "mode": "absolute", 385 | "steps": [ 386 | { 387 | "color": "green", 388 | "value": null 389 | }, 390 | { 391 | "color": "red", 392 | "value": 210 393 | } 394 | ] 395 | } 396 | }, 397 | "overrides": [] 398 | }, 399 | "gridPos": { 400 | "h": 6, 401 | "w": 2, 402 | "x": 12, 403 | "y": 0 404 | }, 405 | "id": 36, 406 | "options": { 407 | "colorMode": "value", 408 | "graphMode": "none", 409 | "justifyMode": "auto", 410 | "orientation": "auto", 411 | "reduceOptions": { 412 | "calcs": [ 413 | "last" 414 | ], 415 | "fields": "", 416 | "values": false 417 | } 418 | }, 419 | "pluginVersion": "7.0.5", 420 | "targets": [ 421 | { 422 | "expr": "greenplum_cluster_total_connections", 423 | "instant": true, 424 | "interval": "", 425 | "legendFormat": "", 426 | "refId": "A" 427 | } 428 | ], 429 | "timeFrom": null, 430 | "timeShift": null, 431 | "title": "当前连接数", 432 | "type": "stat" 433 | }, 434 | { 435 | "datasource": "${DS_PROMETHEUS}", 436 | "description": "Greenplum数据存储节点的剩余可用存储空间", 437 | "fieldConfig": { 438 | "defaults": { 439 | "custom": {}, 440 | "decimals": 2, 441 | "mappings": [], 442 | "thresholds": { 443 | "mode": "absolute", 444 | "steps": [ 445 | { 446 | "color": "green", 447 | "value": null 448 | } 449 | ] 450 | }, 451 | "unit": "decgbytes" 452 | }, 453 | "overrides": [] 454 | }, 455 | "gridPos": { 456 | "h": 6, 457 | "w": 10, 458 | "x": 14, 459 | "y": 0 460 | }, 461 | "id": 4, 462 | "options": { 463 | "colorMode": "value", 464 | "graphMode": "area", 465 | "justifyMode": "auto", 466 | "orientation": "auto", 467 | "reduceOptions": { 468 | "calcs": [ 469 | "lastNotNull" 470 | ], 471 | "fields": "", 472 | "values": false 473 | } 474 | }, 475 | "pluginVersion": "7.0.5", 476 | "targets": [ 477 | { 478 | "expr": "greenplum_node_segment_disk_free_mb_size", 479 | "instant": true, 480 | "interval": "", 481 | "intervalFactor": 1, 482 | "legendFormat": "{{hostname}}", 483 | "refId": "A" 484 | } 485 | ], 486 | "timeFrom": null, 487 | "timeShift": null, 488 | "title": "当前Segment剩余存储空间", 489 | "type": "stat" 490 | }, 491 | { 492 | "datasource": "${DS_PROMETHEUS}", 493 | "description": "数据库配置最近加载时间", 494 | "fieldConfig": { 495 | "defaults": { 496 | "custom": {}, 497 | "mappings": [], 498 | "thresholds": { 499 | "mode": "absolute", 500 | "steps": [ 501 | { 502 | "color": "green", 503 | "value": null 504 | }, 505 | { 506 | "color": "red", 507 | "value": 80 508 | } 509 | ] 510 | }, 511 | "unit": "dateTimeAsIso" 512 | }, 513 | "overrides": [] 514 | }, 515 | "gridPos": { 516 | "h": 4, 517 | "w": 3, 518 | "x": 0, 519 | "y": 4 520 | }, 521 | "id": 28, 522 | "options": { 523 | "colorMode": "value", 524 | "graphMode": "area", 525 | "justifyMode": "auto", 526 | "orientation": "auto", 527 | "reduceOptions": { 528 | "calcs": [ 529 | "lastNotNull" 530 | ], 531 | "fields": "", 532 | "values": false 533 | } 534 | }, 535 | "pluginVersion": "7.0.5", 536 | "targets": [ 537 | { 538 | "expr": "greenplum_cluster_config_last_load_time_seconds*1000", 539 | "format": "time_series", 540 | "instant": true, 541 | "interval": "", 542 | "intervalFactor": 1, 543 | "legendFormat": "", 544 | "refId": "A" 545 | } 546 | ], 547 | "timeFrom": null, 548 | "timeShift": null, 549 | "title": "配置加载时间", 550 | "transformations": [], 551 | "type": "stat" 552 | }, 553 | { 554 | "datasource": "${DS_PROMETHEUS}", 555 | "description": "当前在线连接的账号总个数", 556 | "fieldConfig": { 557 | "defaults": { 558 | "custom": {}, 559 | "mappings": [], 560 | "thresholds": { 561 | "mode": "absolute", 562 | "steps": [ 563 | { 564 | "color": "green", 565 | "value": null 566 | }, 567 | { 568 | "color": "red", 569 | "value": 80 570 | } 571 | ] 572 | } 573 | }, 574 | "overrides": [] 575 | }, 576 | "gridPos": { 577 | "h": 4, 578 | "w": 2, 579 | "x": 3, 580 | "y": 4 581 | }, 582 | "id": 20, 583 | "options": { 584 | "colorMode": "value", 585 | "graphMode": "area", 586 | "justifyMode": "auto", 587 | "orientation": "auto", 588 | "reduceOptions": { 589 | "calcs": [ 590 | "last" 591 | ], 592 | "fields": "", 593 | "values": false 594 | } 595 | }, 596 | "pluginVersion": "7.0.5", 597 | "targets": [ 598 | { 599 | "expr": "greenplum_cluster_total_online_user_count", 600 | "interval": "", 601 | "legendFormat": "", 602 | "refId": "A" 603 | } 604 | ], 605 | "timeFrom": null, 606 | "timeShift": null, 607 | "title": "在线账号数", 608 | "type": "stat" 609 | }, 610 | { 611 | "datasource": "${DS_PROMETHEUS}", 612 | "description": "当前数据库的客户端总数(按IP统计)", 613 | "fieldConfig": { 614 | "defaults": { 615 | "custom": {}, 616 | "decimals": 0, 617 | "mappings": [], 618 | "thresholds": { 619 | "mode": "absolute", 620 | "steps": [ 621 | { 622 | "color": "green", 623 | "value": null 624 | }, 625 | { 626 | "color": "red", 627 | "value": 80 628 | } 629 | ] 630 | } 631 | }, 632 | "overrides": [] 633 | }, 634 | "gridPos": { 635 | "h": 4, 636 | "w": 2, 637 | "x": 5, 638 | "y": 4 639 | }, 640 | "id": 16, 641 | "options": { 642 | "colorMode": "value", 643 | "graphMode": "none", 644 | "justifyMode": "auto", 645 | "orientation": "auto", 646 | "reduceOptions": { 647 | "calcs": [ 648 | "last" 649 | ], 650 | "fields": "", 651 | "values": false 652 | } 653 | }, 654 | "pluginVersion": "7.0.5", 655 | "targets": [ 656 | { 657 | "expr": "greenplum_cluster_total_client_count", 658 | "instant": true, 659 | "interval": "", 660 | "legendFormat": "", 661 | "refId": "A" 662 | } 663 | ], 664 | "timeFrom": null, 665 | "timeShift": null, 666 | "title": "客户端数", 667 | "type": "stat" 668 | }, 669 | { 670 | "datasource": "${DS_PROMETHEUS}", 671 | "description": "数据库当前的用户账号总个数", 672 | "fieldConfig": { 673 | "defaults": { 674 | "custom": {}, 675 | "mappings": [], 676 | "thresholds": { 677 | "mode": "absolute", 678 | "steps": [ 679 | { 680 | "color": "green", 681 | "value": null 682 | }, 683 | { 684 | "color": "red", 685 | "value": 80 686 | } 687 | ] 688 | }, 689 | "unit": "short" 690 | }, 691 | "overrides": [] 692 | }, 693 | "gridPos": { 694 | "h": 4, 695 | "w": 2, 696 | "x": 7, 697 | "y": 4 698 | }, 699 | "id": 14, 700 | "options": { 701 | "colorMode": "value", 702 | "graphMode": "none", 703 | "justifyMode": "auto", 704 | "orientation": "auto", 705 | "reduceOptions": { 706 | "calcs": [ 707 | "lastNotNull" 708 | ], 709 | "fields": "", 710 | "values": false 711 | } 712 | }, 713 | "pluginVersion": "7.0.5", 714 | "targets": [ 715 | { 716 | "expr": "greenplum_server_users_total_count", 717 | "format": "time_series", 718 | "instant": true, 719 | "interval": "", 720 | "legendFormat": "", 721 | "refId": "A" 722 | } 723 | ], 724 | "timeFrom": null, 725 | "timeShift": null, 726 | "title": "账号总数", 727 | "type": "stat" 728 | }, 729 | { 730 | "datasource": "${DS_PROMETHEUS}", 731 | "description": "数据库关注指标", 732 | "fieldConfig": { 733 | "defaults": { 734 | "custom": {}, 735 | "decimals": 2, 736 | "mappings": [], 737 | "thresholds": { 738 | "mode": "absolute", 739 | "steps": [ 740 | { 741 | "color": "green", 742 | "value": null 743 | }, 744 | { 745 | "color": "red", 746 | "value": 100 747 | } 748 | ] 749 | }, 750 | "unit": "percent" 751 | }, 752 | "overrides": [] 753 | }, 754 | "gridPos": { 755 | "h": 4, 756 | "w": 3, 757 | "x": 9, 758 | "y": 4 759 | }, 760 | "id": 46, 761 | "options": { 762 | "colorMode": "value", 763 | "graphMode": "none", 764 | "justifyMode": "center", 765 | "orientation": "auto", 766 | "reduceOptions": { 767 | "calcs": [ 768 | "last" 769 | ], 770 | "fields": "", 771 | "values": false 772 | } 773 | }, 774 | "pluginVersion": "7.0.5", 775 | "targets": [ 776 | { 777 | "expr": "greenplum_server_database_hit_cache_percent_rate", 778 | "interval": "", 779 | "legendFormat": "缓存命中率", 780 | "refId": "A" 781 | }, 782 | { 783 | "expr": "greenplum_server_database_transition_commit_percent_rate", 784 | "interval": "", 785 | "legendFormat": "事务提交率", 786 | "refId": "B" 787 | } 788 | ], 789 | "timeFrom": null, 790 | "timeShift": null, 791 | "title": "", 792 | "type": "stat" 793 | }, 794 | { 795 | "aliasColors": {}, 796 | "bars": false, 797 | "dashLength": 10, 798 | "dashes": false, 799 | "datasource": "${DS_PROMETHEUS}", 800 | "description": "展示客户端连接数的历史趋势信息", 801 | "fieldConfig": { 802 | "defaults": { 803 | "custom": { 804 | "align": null 805 | }, 806 | "mappings": [], 807 | "thresholds": { 808 | "mode": "absolute", 809 | "steps": [ 810 | { 811 | "color": "green", 812 | "value": null 813 | }, 814 | { 815 | "color": "red", 816 | "value": 80 817 | } 818 | ] 819 | } 820 | }, 821 | "overrides": [] 822 | }, 823 | "fill": 1, 824 | "fillGradient": 0, 825 | "gridPos": { 826 | "h": 10, 827 | "w": 12, 828 | "x": 12, 829 | "y": 6 830 | }, 831 | "hiddenSeries": false, 832 | "id": 10, 833 | "legend": { 834 | "alignAsTable": false, 835 | "avg": false, 836 | "current": false, 837 | "hideEmpty": false, 838 | "max": true, 839 | "min": false, 840 | "rightSide": true, 841 | "show": true, 842 | "sideWidth": null, 843 | "total": false, 844 | "values": true 845 | }, 846 | "lines": true, 847 | "linewidth": 1, 848 | "nullPointMode": "null", 849 | "options": { 850 | "dataLinks": [] 851 | }, 852 | "percentage": false, 853 | "pluginVersion": "7.0.5", 854 | "pointradius": 2, 855 | "points": false, 856 | "renderer": "flot", 857 | "seriesOverrides": [], 858 | "spaceLength": 10, 859 | "stack": false, 860 | "steppedLine": false, 861 | "targets": [ 862 | { 863 | "expr": "greenplum_cluster_active_connections_per_client{client!=\"\"}", 864 | "interval": "", 865 | "legendFormat": "{{client}}", 866 | "refId": "A" 867 | } 868 | ], 869 | "thresholds": [], 870 | "timeFrom": null, 871 | "timeRegions": [], 872 | "timeShift": null, 873 | "title": "客户端连接数统计图", 874 | "tooltip": { 875 | "shared": true, 876 | "sort": 0, 877 | "value_type": "individual" 878 | }, 879 | "type": "graph", 880 | "xaxis": { 881 | "buckets": null, 882 | "mode": "time", 883 | "name": null, 884 | "show": true, 885 | "values": [] 886 | }, 887 | "yaxes": [ 888 | { 889 | "decimals": null, 890 | "format": "short", 891 | "label": "连接个数", 892 | "logBase": 1, 893 | "max": "80", 894 | "min": null, 895 | "show": true 896 | }, 897 | { 898 | "format": "short", 899 | "label": null, 900 | "logBase": 1, 901 | "max": null, 902 | "min": null, 903 | "show": false 904 | } 905 | ], 906 | "yaxis": { 907 | "align": false, 908 | "alignLevel": null 909 | } 910 | }, 911 | { 912 | "aliasColors": {}, 913 | "bars": false, 914 | "dashLength": 10, 915 | "dashes": false, 916 | "datasource": "${DS_PROMETHEUS}", 917 | "decimals": 0, 918 | "description": "展示各个账号的活动连接数趋势信息", 919 | "fieldConfig": { 920 | "defaults": { 921 | "custom": {} 922 | }, 923 | "overrides": [] 924 | }, 925 | "fill": 1, 926 | "fillGradient": 0, 927 | "gridPos": { 928 | "h": 8, 929 | "w": 12, 930 | "x": 0, 931 | "y": 8 932 | }, 933 | "hiddenSeries": false, 934 | "id": 6, 935 | "legend": { 936 | "avg": false, 937 | "current": false, 938 | "max": false, 939 | "min": false, 940 | "show": false, 941 | "total": false, 942 | "values": false 943 | }, 944 | "lines": true, 945 | "linewidth": 1, 946 | "nullPointMode": "null", 947 | "options": { 948 | "dataLinks": [] 949 | }, 950 | "percentage": false, 951 | "pointradius": 2, 952 | "points": false, 953 | "renderer": "flot", 954 | "seriesOverrides": [], 955 | "spaceLength": 10, 956 | "stack": false, 957 | "steppedLine": false, 958 | "targets": [ 959 | { 960 | "expr": "greenplum_cluster_active_connections", 961 | "interval": "", 962 | "legendFormat": "{{job}}", 963 | "refId": "A" 964 | } 965 | ], 966 | "thresholds": [], 967 | "timeFrom": null, 968 | "timeRegions": [], 969 | "timeShift": null, 970 | "title": "活动连接数折线历史图", 971 | "tooltip": { 972 | "shared": true, 973 | "sort": 0, 974 | "value_type": "individual" 975 | }, 976 | "type": "graph", 977 | "xaxis": { 978 | "buckets": null, 979 | "mode": "time", 980 | "name": null, 981 | "show": true, 982 | "values": [] 983 | }, 984 | "yaxes": [ 985 | { 986 | "decimals": 0, 987 | "format": "short", 988 | "label": "", 989 | "logBase": 1, 990 | "max": null, 991 | "min": null, 992 | "show": true 993 | }, 994 | { 995 | "format": "short", 996 | "label": null, 997 | "logBase": 1, 998 | "max": null, 999 | "min": null, 1000 | "show": true 1001 | } 1002 | ], 1003 | "yaxis": { 1004 | "align": false, 1005 | "alignLevel": null 1006 | } 1007 | }, 1008 | { 1009 | "aliasColors": {}, 1010 | "bars": false, 1011 | "dashLength": 10, 1012 | "dashes": false, 1013 | "datasource": "${DS_PROMETHEUS}", 1014 | "description": "展示各数据库内表的总数目历史趋势信息", 1015 | "fieldConfig": { 1016 | "defaults": { 1017 | "custom": {}, 1018 | "mappings": [], 1019 | "thresholds": { 1020 | "mode": "absolute", 1021 | "steps": [ 1022 | { 1023 | "color": "green", 1024 | "value": null 1025 | }, 1026 | { 1027 | "color": "red", 1028 | "value": 80 1029 | } 1030 | ] 1031 | } 1032 | }, 1033 | "overrides": [] 1034 | }, 1035 | "fill": 1, 1036 | "fillGradient": 0, 1037 | "gridPos": { 1038 | "h": 8, 1039 | "w": 12, 1040 | "x": 0, 1041 | "y": 16 1042 | }, 1043 | "hiddenSeries": false, 1044 | "id": 42, 1045 | "legend": { 1046 | "alignAsTable": false, 1047 | "avg": false, 1048 | "current": false, 1049 | "max": false, 1050 | "min": false, 1051 | "rightSide": true, 1052 | "show": true, 1053 | "total": false, 1054 | "values": false 1055 | }, 1056 | "lines": true, 1057 | "linewidth": 3, 1058 | "nullPointMode": "null", 1059 | "options": { 1060 | "dataLinks": [] 1061 | }, 1062 | "percentage": false, 1063 | "pluginVersion": "7.0.5", 1064 | "pointradius": 2, 1065 | "points": false, 1066 | "renderer": "flot", 1067 | "seriesOverrides": [], 1068 | "spaceLength": 10, 1069 | "stack": false, 1070 | "steppedLine": false, 1071 | "targets": [ 1072 | { 1073 | "expr": "greenplum_node_database_table_total_count", 1074 | "interval": "", 1075 | "legendFormat": "{{dbname}}", 1076 | "refId": "A" 1077 | } 1078 | ], 1079 | "thresholds": [], 1080 | "timeFrom": null, 1081 | "timeRegions": [], 1082 | "timeShift": null, 1083 | "title": "各数据库内表的总数目", 1084 | "tooltip": { 1085 | "shared": true, 1086 | "sort": 0, 1087 | "value_type": "individual" 1088 | }, 1089 | "type": "graph", 1090 | "xaxis": { 1091 | "buckets": null, 1092 | "mode": "time", 1093 | "name": null, 1094 | "show": true, 1095 | "values": [] 1096 | }, 1097 | "yaxes": [ 1098 | { 1099 | "format": "short", 1100 | "label": null, 1101 | "logBase": 1, 1102 | "max": null, 1103 | "min": null, 1104 | "show": true 1105 | }, 1106 | { 1107 | "format": "short", 1108 | "label": null, 1109 | "logBase": 1, 1110 | "max": null, 1111 | "min": null, 1112 | "show": true 1113 | } 1114 | ], 1115 | "yaxis": { 1116 | "align": false, 1117 | "alignLevel": null 1118 | } 1119 | }, 1120 | { 1121 | "aliasColors": {}, 1122 | "bars": false, 1123 | "dashLength": 10, 1124 | "dashes": false, 1125 | "datasource": "${DS_PROMETHEUS}", 1126 | "description": "展示在线账号连接数的历史趋势信息", 1127 | "fieldConfig": { 1128 | "defaults": { 1129 | "custom": {}, 1130 | "mappings": [], 1131 | "thresholds": { 1132 | "mode": "absolute", 1133 | "steps": [ 1134 | { 1135 | "color": "green", 1136 | "value": null 1137 | }, 1138 | { 1139 | "color": "red", 1140 | "value": 80 1141 | } 1142 | ] 1143 | } 1144 | }, 1145 | "overrides": [] 1146 | }, 1147 | "fill": 1, 1148 | "fillGradient": 2, 1149 | "gridPos": { 1150 | "h": 8, 1151 | "w": 12, 1152 | "x": 12, 1153 | "y": 16 1154 | }, 1155 | "hiddenSeries": false, 1156 | "id": 12, 1157 | "legend": { 1158 | "alignAsTable": true, 1159 | "avg": false, 1160 | "current": false, 1161 | "max": false, 1162 | "min": false, 1163 | "rightSide": true, 1164 | "show": true, 1165 | "total": false, 1166 | "values": false 1167 | }, 1168 | "lines": true, 1169 | "linewidth": 5, 1170 | "nullPointMode": "null", 1171 | "options": { 1172 | "dataLinks": [] 1173 | }, 1174 | "percentage": false, 1175 | "pluginVersion": "7.0.5", 1176 | "pointradius": 2, 1177 | "points": false, 1178 | "renderer": "flot", 1179 | "seriesOverrides": [], 1180 | "spaceLength": 10, 1181 | "stack": false, 1182 | "steppedLine": false, 1183 | "targets": [ 1184 | { 1185 | "expr": "greenplum_cluster_active_connections_per_user", 1186 | "interval": "", 1187 | "legendFormat": "{{usename}}", 1188 | "refId": "A" 1189 | } 1190 | ], 1191 | "thresholds": [], 1192 | "timeFrom": null, 1193 | "timeRegions": [], 1194 | "timeShift": null, 1195 | "title": "在线账号连接数分析图", 1196 | "tooltip": { 1197 | "shared": true, 1198 | "sort": 0, 1199 | "value_type": "individual" 1200 | }, 1201 | "type": "graph", 1202 | "xaxis": { 1203 | "buckets": null, 1204 | "mode": "time", 1205 | "name": null, 1206 | "show": true, 1207 | "values": [] 1208 | }, 1209 | "yaxes": [ 1210 | { 1211 | "format": "short", 1212 | "label": "连接数", 1213 | "logBase": 1, 1214 | "max": null, 1215 | "min": null, 1216 | "show": true 1217 | }, 1218 | { 1219 | "format": "short", 1220 | "label": null, 1221 | "logBase": 1, 1222 | "max": null, 1223 | "min": null, 1224 | "show": true 1225 | } 1226 | ], 1227 | "yaxis": { 1228 | "align": false, 1229 | "alignLevel": null 1230 | } 1231 | }, 1232 | { 1233 | "datasource": "${DS_PROMETHEUS}", 1234 | "description": "展示集群当前各库内表总数目", 1235 | "fieldConfig": { 1236 | "defaults": { 1237 | "custom": {}, 1238 | "mappings": [], 1239 | "thresholds": { 1240 | "mode": "absolute", 1241 | "steps": [ 1242 | { 1243 | "color": "green", 1244 | "value": null 1245 | }, 1246 | { 1247 | "color": "red", 1248 | "value": 5000 1249 | } 1250 | ] 1251 | } 1252 | }, 1253 | "overrides": [] 1254 | }, 1255 | "gridPos": { 1256 | "h": 9, 1257 | "w": 12, 1258 | "x": 0, 1259 | "y": 24 1260 | }, 1261 | "id": 44, 1262 | "options": { 1263 | "displayMode": "lcd", 1264 | "orientation": "horizontal", 1265 | "reduceOptions": { 1266 | "calcs": [ 1267 | "last" 1268 | ], 1269 | "fields": "", 1270 | "values": false 1271 | }, 1272 | "showUnfilled": true 1273 | }, 1274 | "pluginVersion": "7.0.5", 1275 | "targets": [ 1276 | { 1277 | "expr": "greenplum_node_database_table_total_count", 1278 | "instant": true, 1279 | "interval": "", 1280 | "legendFormat": "{{dbname}}", 1281 | "refId": "A" 1282 | } 1283 | ], 1284 | "timeFrom": null, 1285 | "timeShift": null, 1286 | "title": "当前各库内表总数目", 1287 | "type": "bargauge" 1288 | }, 1289 | { 1290 | "datasource": "${DS_PROMETHEUS}", 1291 | "description": "展示当前各库占用数据存储空间大小的柱状图", 1292 | "fieldConfig": { 1293 | "defaults": { 1294 | "custom": {}, 1295 | "mappings": [], 1296 | "thresholds": { 1297 | "mode": "absolute", 1298 | "steps": [ 1299 | { 1300 | "color": "green", 1301 | "value": null 1302 | }, 1303 | { 1304 | "color": "red", 1305 | "value": 8000 1306 | } 1307 | ] 1308 | }, 1309 | "unit": "decmbytes" 1310 | }, 1311 | "overrides": [] 1312 | }, 1313 | "gridPos": { 1314 | "h": 9, 1315 | "w": 12, 1316 | "x": 12, 1317 | "y": 24 1318 | }, 1319 | "id": 2, 1320 | "options": { 1321 | "displayMode": "gradient", 1322 | "orientation": "auto", 1323 | "reduceOptions": { 1324 | "calcs": [ 1325 | "mean" 1326 | ], 1327 | "fields": "", 1328 | "values": false 1329 | }, 1330 | "showUnfilled": false 1331 | }, 1332 | "pluginVersion": "7.0.5", 1333 | "targets": [ 1334 | { 1335 | "expr": "greenplum_node_database_name_mb_size", 1336 | "instant": true, 1337 | "interval": "", 1338 | "legendFormat": "{{dbname}}", 1339 | "refId": "A" 1340 | } 1341 | ], 1342 | "timeFrom": null, 1343 | "timeShift": null, 1344 | "title": "各库存储空间统计图", 1345 | "type": "bargauge" 1346 | }, 1347 | { 1348 | "cacheTimeout": null, 1349 | "datasource": "${DS_PROMETHEUS}", 1350 | "description": "列举集群的Segment实例在线状态", 1351 | "fieldConfig": { 1352 | "defaults": { 1353 | "custom": { 1354 | "align": "center", 1355 | "displayMode": "color-text" 1356 | }, 1357 | "mappings": [ 1358 | { 1359 | "from": "", 1360 | "id": 0, 1361 | "operator": "", 1362 | "text": "DOWN", 1363 | "to": "", 1364 | "type": 1, 1365 | "value": "0" 1366 | }, 1367 | { 1368 | "from": "", 1369 | "id": 1, 1370 | "operator": "", 1371 | "text": "UP", 1372 | "to": "", 1373 | "type": 1, 1374 | "value": "1" 1375 | } 1376 | ], 1377 | "thresholds": { 1378 | "mode": "absolute", 1379 | "steps": [ 1380 | { 1381 | "color": "green", 1382 | "value": null 1383 | }, 1384 | { 1385 | "color": "red", 1386 | "value": 80 1387 | } 1388 | ] 1389 | } 1390 | }, 1391 | "overrides": [] 1392 | }, 1393 | "gridPos": { 1394 | "h": 13, 1395 | "w": 12, 1396 | "x": 0, 1397 | "y": 33 1398 | }, 1399 | "id": 24, 1400 | "interval": "", 1401 | "links": [], 1402 | "options": { 1403 | "showHeader": true, 1404 | "sortBy": [ 1405 | { 1406 | "desc": false, 1407 | "displayName": "port" 1408 | } 1409 | ] 1410 | }, 1411 | "pluginVersion": "7.0.5", 1412 | "repeat": null, 1413 | "targets": [ 1414 | { 1415 | "expr": "greenplum_node_segment_status", 1416 | "format": "table", 1417 | "instant": true, 1418 | "interval": "", 1419 | "intervalFactor": 10, 1420 | "legendFormat": "", 1421 | "refId": "A" 1422 | } 1423 | ], 1424 | "timeFrom": null, 1425 | "timeShift": null, 1426 | "title": "Segment实例在线状态", 1427 | "transformations": [ 1428 | { 1429 | "id": "filterFieldsByName", 1430 | "options": { 1431 | "include": { 1432 | "names": [ 1433 | "hostname", 1434 | "port", 1435 | "preferred_role", 1436 | "Value" 1437 | ] 1438 | } 1439 | } 1440 | }, 1441 | { 1442 | "id": "organize", 1443 | "options": { 1444 | "excludeByName": { 1445 | "hostname": false 1446 | }, 1447 | "indexByName": { 1448 | "Value": 3, 1449 | "hostname": 0, 1450 | "port": 1, 1451 | "preferred_role": 2 1452 | }, 1453 | "renameByName": { 1454 | "Value": "状态", 1455 | "hostname": "主机", 1456 | "port": "端口", 1457 | "preferred_role": "角色" 1458 | } 1459 | } 1460 | } 1461 | ], 1462 | "type": "table" 1463 | }, 1464 | { 1465 | "datasource": "${DS_PROMETHEUS}", 1466 | "description": "列举集群的Segment实例同步模式", 1467 | "fieldConfig": { 1468 | "defaults": { 1469 | "custom": { 1470 | "align": "center", 1471 | "displayMode": "color-text" 1472 | }, 1473 | "mappings": [ 1474 | { 1475 | "from": "", 1476 | "id": 0, 1477 | "operator": "", 1478 | "text": "synchronized", 1479 | "to": "", 1480 | "type": 1, 1481 | "value": "1" 1482 | }, 1483 | { 1484 | "from": "", 1485 | "id": 1, 1486 | "operator": "", 1487 | "text": "resyncing", 1488 | "to": "", 1489 | "type": 1, 1490 | "value": "2" 1491 | }, 1492 | { 1493 | "from": "", 1494 | "id": 2, 1495 | "operator": "", 1496 | "text": "change logging", 1497 | "to": "", 1498 | "type": 1, 1499 | "value": "3" 1500 | }, 1501 | { 1502 | "from": "", 1503 | "id": 3, 1504 | "operator": "", 1505 | "text": "not synchronized", 1506 | "to": "", 1507 | "type": 1, 1508 | "value": "4" 1509 | } 1510 | ], 1511 | "thresholds": { 1512 | "mode": "absolute", 1513 | "steps": [ 1514 | { 1515 | "color": "green", 1516 | "value": null 1517 | }, 1518 | { 1519 | "color": "red", 1520 | "value": 80 1521 | } 1522 | ] 1523 | } 1524 | }, 1525 | "overrides": [] 1526 | }, 1527 | "gridPos": { 1528 | "h": 13, 1529 | "w": 12, 1530 | "x": 12, 1531 | "y": 33 1532 | }, 1533 | "id": 26, 1534 | "options": { 1535 | "showHeader": true 1536 | }, 1537 | "pluginVersion": "7.0.5", 1538 | "repeat": null, 1539 | "targets": [ 1540 | { 1541 | "expr": "greenplum_node_segment_mode", 1542 | "format": "table", 1543 | "instant": true, 1544 | "interval": "", 1545 | "intervalFactor": 10, 1546 | "legendFormat": "", 1547 | "refId": "A" 1548 | } 1549 | ], 1550 | "timeFrom": null, 1551 | "timeShift": null, 1552 | "title": "Segment实例同步模式", 1553 | "transformations": [ 1554 | { 1555 | "id": "filterFieldsByName", 1556 | "options": { 1557 | "include": { 1558 | "names": [ 1559 | "hostname", 1560 | "port", 1561 | "preferred_role", 1562 | "Value" 1563 | ] 1564 | } 1565 | } 1566 | }, 1567 | { 1568 | "id": "organize", 1569 | "options": { 1570 | "excludeByName": {}, 1571 | "indexByName": { 1572 | "Value": 4, 1573 | "dbid": 2, 1574 | "hostname": 0, 1575 | "port": 1, 1576 | "preferred_role": 3 1577 | }, 1578 | "renameByName": { 1579 | "Value": "状态", 1580 | "dbid": "数据库ID", 1581 | "hostname": "主机", 1582 | "port": "端口", 1583 | "preferred_role": "角色" 1584 | } 1585 | } 1586 | } 1587 | ], 1588 | "type": "table" 1589 | }, 1590 | { 1591 | "datasource": "${DS_PROMETHEUS}", 1592 | "description": "列举了当前监听的客户端访问数据库操作时的锁信息", 1593 | "fieldConfig": { 1594 | "defaults": { 1595 | "custom": { 1596 | "align": null, 1597 | "displayMode": "color-text" 1598 | }, 1599 | "mappings": [], 1600 | "thresholds": { 1601 | "mode": "absolute", 1602 | "steps": [ 1603 | { 1604 | "color": "green", 1605 | "value": null 1606 | }, 1607 | { 1608 | "color": "red", 1609 | "value": 80 1610 | } 1611 | ] 1612 | } 1613 | }, 1614 | "overrides": [ 1615 | { 1616 | "matcher": { 1617 | "id": "byName", 1618 | "options": "查询语句" 1619 | }, 1620 | "properties": [ 1621 | { 1622 | "id": "custom.width", 1623 | "value": 361 1624 | } 1625 | ] 1626 | }, 1627 | { 1628 | "matcher": { 1629 | "id": "byName", 1630 | "options": "数据库名称" 1631 | }, 1632 | "properties": [ 1633 | { 1634 | "id": "custom.width", 1635 | "value": 94 1636 | } 1637 | ] 1638 | }, 1639 | { 1640 | "matcher": { 1641 | "id": "byName", 1642 | "options": "锁状态" 1643 | }, 1644 | "properties": [ 1645 | { 1646 | "id": "custom.width", 1647 | "value": 91 1648 | } 1649 | ] 1650 | }, 1651 | { 1652 | "matcher": { 1653 | "id": "byName", 1654 | "options": "锁对象" 1655 | }, 1656 | "properties": [ 1657 | { 1658 | "id": "custom.width", 1659 | "value": 72 1660 | } 1661 | ] 1662 | } 1663 | ] 1664 | }, 1665 | "gridPos": { 1666 | "h": 8, 1667 | "w": 12, 1668 | "x": 0, 1669 | "y": 46 1670 | }, 1671 | "id": 32, 1672 | "options": { 1673 | "showHeader": true, 1674 | "sortBy": [] 1675 | }, 1676 | "pluginVersion": "7.0.5", 1677 | "targets": [ 1678 | { 1679 | "expr": "greenplum_server_locks_table_detail", 1680 | "format": "table", 1681 | "instant": true, 1682 | "interval": "", 1683 | "legendFormat": "", 1684 | "refId": "A" 1685 | } 1686 | ], 1687 | "timeFrom": null, 1688 | "timeShift": null, 1689 | "title": "数据库锁状态监听实时列表", 1690 | "transformations": [ 1691 | { 1692 | "id": "filterFieldsByName", 1693 | "options": { 1694 | "include": { 1695 | "names": [ 1696 | "application_name", 1697 | "datname", 1698 | "lock_satus", 1699 | "locktype", 1700 | "mode", 1701 | "pid", 1702 | "query", 1703 | "state", 1704 | "usename" 1705 | ] 1706 | } 1707 | } 1708 | }, 1709 | { 1710 | "id": "organize", 1711 | "options": { 1712 | "excludeByName": {}, 1713 | "indexByName": {}, 1714 | "renameByName": { 1715 | "Value": "开始时间", 1716 | "application_name": "应用名称", 1717 | "datname": "数据库名称", 1718 | "lock_satus": "锁状态", 1719 | "locktype": "锁对象", 1720 | "mode": "锁类型", 1721 | "pid": "进程ID", 1722 | "query": "查询语句", 1723 | "state": "连接状态", 1724 | "usename": "操作账号" 1725 | } 1726 | } 1727 | } 1728 | ], 1729 | "type": "table" 1730 | }, 1731 | { 1732 | "aliasColors": {}, 1733 | "bars": false, 1734 | "dashLength": 10, 1735 | "dashes": false, 1736 | "datasource": "${DS_PROMETHEUS}", 1737 | "fieldConfig": { 1738 | "defaults": { 1739 | "custom": {} 1740 | }, 1741 | "overrides": [] 1742 | }, 1743 | "fill": 1, 1744 | "fillGradient": 0, 1745 | "gridPos": { 1746 | "h": 8, 1747 | "w": 12, 1748 | "x": 12, 1749 | "y": 46 1750 | }, 1751 | "hiddenSeries": false, 1752 | "id": 30, 1753 | "legend": { 1754 | "avg": false, 1755 | "current": false, 1756 | "max": false, 1757 | "min": false, 1758 | "rightSide": true, 1759 | "show": true, 1760 | "sideWidth": null, 1761 | "total": false, 1762 | "values": false 1763 | }, 1764 | "lines": true, 1765 | "linewidth": 5, 1766 | "nullPointMode": "null", 1767 | "options": { 1768 | "dataLinks": [] 1769 | }, 1770 | "percentage": false, 1771 | "pointradius": 2, 1772 | "points": false, 1773 | "renderer": "flot", 1774 | "seriesOverrides": [], 1775 | "spaceLength": 10, 1776 | "stack": false, 1777 | "steppedLine": false, 1778 | "targets": [ 1779 | { 1780 | "expr": "rate(greenplum_node_database_name_mb_size[20m])", 1781 | "interval": "", 1782 | "legendFormat": "{{dbname}}", 1783 | "refId": "A" 1784 | } 1785 | ], 1786 | "thresholds": [], 1787 | "timeFrom": null, 1788 | "timeRegions": [], 1789 | "timeShift": null, 1790 | "title": "各库耗用存储瞬时速率", 1791 | "tooltip": { 1792 | "shared": true, 1793 | "sort": 0, 1794 | "value_type": "individual" 1795 | }, 1796 | "type": "graph", 1797 | "xaxis": { 1798 | "buckets": null, 1799 | "mode": "time", 1800 | "name": null, 1801 | "show": true, 1802 | "values": [] 1803 | }, 1804 | "yaxes": [ 1805 | { 1806 | "format": "short", 1807 | "label": null, 1808 | "logBase": 1, 1809 | "max": null, 1810 | "min": null, 1811 | "show": true 1812 | }, 1813 | { 1814 | "format": "short", 1815 | "label": null, 1816 | "logBase": 1, 1817 | "max": null, 1818 | "min": null, 1819 | "show": true 1820 | } 1821 | ], 1822 | "yaxis": { 1823 | "align": false, 1824 | "alignLevel": null 1825 | } 1826 | }, 1827 | { 1828 | "datasource": "${DS_PROMETHEUS}", 1829 | "description": "列举存在膨胀数据表的信息(需要数据库后端定期或定时使用analyzedb工具分析生成统计数据)", 1830 | "fieldConfig": { 1831 | "defaults": { 1832 | "custom": { 1833 | "align": null, 1834 | "displayMode": "color-text" 1835 | }, 1836 | "mappings": [ 1837 | { 1838 | "from": "", 1839 | "id": 0, 1840 | "operator": "", 1841 | "text": "严重膨胀", 1842 | "to": "", 1843 | "type": 1, 1844 | "value": "2" 1845 | }, 1846 | { 1847 | "from": "", 1848 | "id": 1, 1849 | "operator": "", 1850 | "text": "中度膨胀", 1851 | "to": "", 1852 | "type": 1, 1853 | "value": "1" 1854 | }, 1855 | { 1856 | "from": "", 1857 | "id": 2, 1858 | "operator": "", 1859 | "text": "轻微膨胀", 1860 | "to": "", 1861 | "type": 1, 1862 | "value": "0" 1863 | } 1864 | ], 1865 | "thresholds": { 1866 | "mode": "absolute", 1867 | "steps": [ 1868 | { 1869 | "color": "green", 1870 | "value": null 1871 | }, 1872 | { 1873 | "color": "red", 1874 | "value": 2 1875 | } 1876 | ] 1877 | } 1878 | }, 1879 | "overrides": [ 1880 | { 1881 | "matcher": { 1882 | "id": "byName", 1883 | "options": "期望页数" 1884 | }, 1885 | "properties": [ 1886 | { 1887 | "id": "custom.width", 1888 | "value": 71 1889 | } 1890 | ] 1891 | }, 1892 | { 1893 | "matcher": { 1894 | "id": "byName", 1895 | "options": "实际页数" 1896 | }, 1897 | "properties": [ 1898 | { 1899 | "id": "custom.width", 1900 | "value": 116 1901 | } 1902 | ] 1903 | } 1904 | ] 1905 | }, 1906 | "gridPos": { 1907 | "h": 8, 1908 | "w": 12, 1909 | "x": 0, 1910 | "y": 54 1911 | }, 1912 | "id": 48, 1913 | "options": { 1914 | "showHeader": true, 1915 | "sortBy": [] 1916 | }, 1917 | "pluginVersion": "7.0.5", 1918 | "targets": [ 1919 | { 1920 | "expr": "greenplum_server_database_table_bloat_list", 1921 | "format": "table", 1922 | "instant": true, 1923 | "interval": "", 1924 | "legendFormat": "", 1925 | "refId": "A" 1926 | } 1927 | ], 1928 | "timeFrom": null, 1929 | "timeShift": null, 1930 | "title": "膨胀垃圾数据列表", 1931 | "transformations": [ 1932 | { 1933 | "id": "organize", 1934 | "options": { 1935 | "excludeByName": {}, 1936 | "indexByName": { 1937 | "Time": 6, 1938 | "Value": 5, 1939 | "__name__": 7, 1940 | "dbname": 0, 1941 | "exppages": 3, 1942 | "instance": 8, 1943 | "job": 9, 1944 | "relpages": 4, 1945 | "schema": 1, 1946 | "table": 2 1947 | }, 1948 | "renameByName": { 1949 | "Value": "膨胀状态", 1950 | "__name__": "", 1951 | "dbname": "数据库", 1952 | "exppages": "期望页数", 1953 | "relpages": "实际页数", 1954 | "schema": "模式名", 1955 | "table": "表名" 1956 | } 1957 | } 1958 | }, 1959 | { 1960 | "id": "filterFieldsByName", 1961 | "options": { 1962 | "include": { 1963 | "names": [ 1964 | "数据库", 1965 | "期望页数", 1966 | "实际页数", 1967 | "模式名", 1968 | "表名", 1969 | "膨胀状态" 1970 | ] 1971 | } 1972 | } 1973 | } 1974 | ], 1975 | "type": "table" 1976 | }, 1977 | { 1978 | "datasource": "${DS_PROMETHEUS}", 1979 | "description": "描述表数据均匀分布在各个存储节点的程度,这里只展示数据总大小大于1024M且倾斜率大于1.5%的表。", 1980 | "fieldConfig": { 1981 | "defaults": { 1982 | "custom": { 1983 | "align": null, 1984 | "displayMode": "color-background" 1985 | }, 1986 | "decimals": 2, 1987 | "mappings": [], 1988 | "thresholds": { 1989 | "mode": "absolute", 1990 | "steps": [ 1991 | { 1992 | "color": "green", 1993 | "value": null 1994 | }, 1995 | { 1996 | "color": "red", 1997 | "value": 5 1998 | } 1999 | ] 2000 | }, 2001 | "unit": "percent" 2002 | }, 2003 | "overrides": [] 2004 | }, 2005 | "gridPos": { 2006 | "h": 8, 2007 | "w": 12, 2008 | "x": 12, 2009 | "y": 54 2010 | }, 2011 | "id": 50, 2012 | "options": { 2013 | "showHeader": true 2014 | }, 2015 | "pluginVersion": "7.0.5", 2016 | "targets": [ 2017 | { 2018 | "expr": "greenplum_server_database_table_skew_list", 2019 | "format": "table", 2020 | "instant": true, 2021 | "interval": "", 2022 | "legendFormat": "", 2023 | "refId": "A" 2024 | } 2025 | ], 2026 | "timeFrom": null, 2027 | "timeShift": null, 2028 | "title": "存储分布倾斜数据列表", 2029 | "transformations": [ 2030 | { 2031 | "id": "organize", 2032 | "options": { 2033 | "excludeByName": {}, 2034 | "indexByName": { 2035 | "Time": 5, 2036 | "Value": 4, 2037 | "__name__": 6, 2038 | "dbname": 0, 2039 | "instance": 7, 2040 | "job": 8, 2041 | "schema": 1, 2042 | "size": 3, 2043 | "table": 2 2044 | }, 2045 | "renameByName": { 2046 | "Time": "", 2047 | "Value": "倾斜率", 2048 | "__name__": "", 2049 | "dbname": "数据库名", 2050 | "instance": "", 2051 | "job": "", 2052 | "schema": "模式名称", 2053 | "size": "表大小", 2054 | "table": "表名称" 2055 | } 2056 | } 2057 | }, 2058 | { 2059 | "id": "filterFieldsByName", 2060 | "options": { 2061 | "include": { 2062 | "names": [ 2063 | "数据库名", 2064 | "模式名称", 2065 | "表名称", 2066 | "表大小", 2067 | "倾斜率" 2068 | ] 2069 | } 2070 | } 2071 | } 2072 | ], 2073 | "type": "table" 2074 | } 2075 | ], 2076 | "refresh": "5s", 2077 | "schemaVersion": 25, 2078 | "style": "dark", 2079 | "tags": [], 2080 | "templating": { 2081 | "list": [ 2082 | { 2083 | "allValue": null, 2084 | "current": { 2085 | "selected": false, 2086 | "text": "6.10.1", 2087 | "value": "6.10.1" 2088 | }, 2089 | "datasource": "${DS_PROMETHEUS}", 2090 | "definition": "label_values(greenplum_cluster_state, version)", 2091 | "hide": 2, 2092 | "includeAll": false, 2093 | "label": null, 2094 | "multi": false, 2095 | "name": "version", 2096 | "options": [], 2097 | "query": "label_values(greenplum_cluster_state, version)", 2098 | "refresh": 2, 2099 | "regex": "", 2100 | "skipUrlSync": false, 2101 | "sort": 0, 2102 | "tagValuesQuery": "", 2103 | "tags": [], 2104 | "tagsQuery": "", 2105 | "type": "query", 2106 | "useTags": false 2107 | } 2108 | ] 2109 | }, 2110 | "time": { 2111 | "from": "now-6h", 2112 | "to": "now" 2113 | }, 2114 | "timepicker": { 2115 | "refresh_intervals": [ 2116 | "10s", 2117 | "30s", 2118 | "1m", 2119 | "5m", 2120 | "15m", 2121 | "30m", 2122 | "1h", 2123 | "2h", 2124 | "1d" 2125 | ] 2126 | }, 2127 | "timezone": "", 2128 | "title": "Greenplum集群监控状态图", 2129 | "uid": "wsZrd9MMk", 2130 | "version": 3, 2131 | "description": "Greenplum集群监控状态图" 2132 | } 2133 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "greenplum-exporter/collector" 5 | "net/http" 6 | 7 | "github.com/prometheus/client_golang/prometheus" 8 | "github.com/prometheus/client_golang/prometheus/promhttp" 9 | logger "github.com/prometheus/common/log" 10 | "gopkg.in/alecthomas/kingpin.v2" 11 | ) 12 | 13 | /** 14 | * 参考教程:https://www.cnblogs.com/momoyan/p/9943268.html 15 | * 官方文档:https://godoc.org/github.com/prometheus/client_golang/prometheus 16 | * 官方文档:https://gp-docs-cn.github.io/docs/admin_guide/monitoring/monitoring.html 17 | */ 18 | 19 | var ( 20 | listenAddress = kingpin.Flag("web.listen-address", "web endpoint").Default("0.0.0.0:9297").String() 21 | metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() 22 | disableDefaultMetrics = kingpin.Flag("disableDefaultMetrics", "do not report default metrics(go metrics and process metrics)").Default("true").Bool() 23 | ) 24 | 25 | var scrapers = map[collector.Scraper]bool{ 26 | collector.NewClusterStateScraper(): true, 27 | collector.NewSegmentScraper(): true, 28 | collector.NewDatabaseSizeScraper(): true, 29 | collector.NewLocksScraper(): true, 30 | collector.NewConnectionsScraper(): true, 31 | collector.NewMaxConnScraper(): true, 32 | collector.NewConnDetailScraper(): true, 33 | collector.NewUsersScraper(): false, 34 | collector.NewBgWriterStateScraper(): false, 35 | 36 | collector.NewSystemScraper(): false, 37 | collector.NewQueryScraper(): false, 38 | collector.NewDynamicMemoryScraper(): false, 39 | collector.NewDiskScraper(): false, 40 | } 41 | 42 | var gathers prometheus.Gatherers 43 | 44 | func main() { 45 | kingpin.Version("1.1.1") 46 | kingpin.HelpFlag.Short('h') 47 | 48 | logger.AddFlags(kingpin.CommandLine) 49 | kingpin.Parse() 50 | 51 | metricsHandleFunc := newHandler(*disableDefaultMetrics, scrapers) 52 | 53 | mux := http.NewServeMux() 54 | 55 | mux.HandleFunc(*metricPath, metricsHandleFunc) 56 | 57 | logger.Warnf("Greenplum exporter is starting and will listening on : %s", *listenAddress) 58 | 59 | logger.Error(http.ListenAndServe(*listenAddress, mux).Error()) 60 | } 61 | 62 | func newHandler(disableDefaultMetrics bool, scrapers map[collector.Scraper]bool) http.HandlerFunc { 63 | 64 | registry := prometheus.NewRegistry() 65 | 66 | enabledScrapers := make([]collector.Scraper, 0, 16) 67 | 68 | for scraper, enable := range scrapers { 69 | if enable { 70 | enabledScrapers = append(enabledScrapers, scraper) 71 | } 72 | } 73 | 74 | greenPlumCollector := collector.NewCollector(enabledScrapers) 75 | 76 | registry.MustRegister(greenPlumCollector) 77 | 78 | if disableDefaultMetrics { 79 | gathers = prometheus.Gatherers{registry} 80 | } else { 81 | gathers = prometheus.Gatherers{registry, prometheus.DefaultGatherer} 82 | } 83 | 84 | handler := promhttp.HandlerFor(gathers, promhttp.HandlerOpts{ 85 | ErrorHandling: promhttp.ContinueOnError, 86 | }) 87 | 88 | return handler.ServeHTTP 89 | } 90 | -------------------------------------------------------------------------------- /stopwatch/stopwatch.go: -------------------------------------------------------------------------------- 1 | package stopwatch 2 | 3 | import ( 4 | "bytes" 5 | "container/list" 6 | "errors" 7 | "fmt" 8 | "time" 9 | ) 10 | 11 | type taskInfo struct { 12 | taskName string 13 | taskElapsed int64 14 | } 15 | 16 | type StopWatch struct { 17 | id string 18 | latestTaskName string 19 | taskList *list.List 20 | latestStartTime time.Time 21 | taskCnt int 22 | totalElapsed int64 23 | } 24 | 25 | func New(id string) *StopWatch { 26 | return &StopWatch{id: id, taskList: list.New()} 27 | } 28 | 29 | func (w *StopWatch) Start(taskName string) error { 30 | if taskName == "" { 31 | return errors.New("task name must not be empty") 32 | } 33 | 34 | if w.latestTaskName != "" { 35 | return fmt.Errorf("can not start new stopwatch, current task: %s is running", w.latestTaskName) 36 | } 37 | 38 | w.latestTaskName = taskName 39 | w.latestStartTime = time.Now() 40 | 41 | return nil 42 | } 43 | 44 | func (w *StopWatch) MustStart(taskName string) { 45 | err := w.Start(taskName) 46 | 47 | if err != nil { 48 | panic(err) 49 | } 50 | } 51 | 52 | func (w *StopWatch) Stop() error { 53 | if w.latestTaskName == "" { 54 | return errors.New("can not stop StopWatch: it's not running") 55 | } 56 | 57 | elapsed := time.Since(w.latestStartTime).Nanoseconds() 58 | 59 | w.totalElapsed += elapsed 60 | lastTask := taskInfo{taskName: w.latestTaskName, taskElapsed: elapsed} 61 | w.taskList.PushBack(lastTask) 62 | w.taskCnt++ 63 | w.latestTaskName = "" 64 | 65 | return nil 66 | } 67 | 68 | func (w *StopWatch) MustStop() { 69 | if err := w.Stop(); err != nil { 70 | panic(err) 71 | } 72 | } 73 | 74 | func (w *StopWatch) ShortSummary() string { 75 | return fmt.Sprintf("StopWatch '"+w.id+"': running time (ms) = %d\n", w.totalElapsed/1000000) 76 | } 77 | 78 | func (w *StopWatch) PrettyPrint() string { 79 | var buf bytes.Buffer 80 | 81 | buf.WriteString(w.ShortSummary()) 82 | 83 | buf.WriteString("-----------------------------------------\n") 84 | buf.WriteString("ms % Task name\n") 85 | buf.WriteString("-----------------------------------------\n") 86 | 87 | for e := w.taskList.Front(); e != nil; e = e.Next() { 88 | taskInfo := e.Value.(taskInfo) 89 | 90 | var elapsed int64 91 | if w.totalElapsed != 0 { 92 | elapsed = taskInfo.taskElapsed*100/w.totalElapsed 93 | } 94 | 95 | buf.WriteString(fmt.Sprintf("%-10d", taskInfo.taskElapsed/1000000)) 96 | buf.WriteString(fmt.Sprintf("%-10s", fmt.Sprintf("%d%%", elapsed))) 97 | buf.WriteString(fmt.Sprintf("%s\n", taskInfo.taskName)) 98 | } 99 | return buf.String() 100 | } 101 | 102 | func (w *StopWatch) Clear() { 103 | w.taskList = list.New() 104 | w.totalElapsed = 0 105 | w.latestTaskName = "" 106 | w.taskCnt = 0 107 | w.id = "" 108 | } --------------------------------------------------------------------------------