pangolin_exporter
211 строк · 6.3 Кб
1// Copyright 2023 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package collector
15
16import (
17"context"
18"database/sql"
19
20"github.com/blang/semver/v4"
21"github.com/go-kit/log"
22"github.com/prometheus/client_golang/prometheus"
23)
24
25const statStatementsSubsystem = "stat_statements"
26
27func init() {
28// WARNING:
29// Disabled by default because this set of metrics can be quite expensive on a busy server
30// Every unique query will cause a new timeseries to be created
31registerCollector(statStatementsSubsystem, defaultDisabled, NewPGStatStatementsCollector)
32}
33
34type PGStatStatementsCollector struct {
35log log.Logger
36}
37
38func NewPGStatStatementsCollector(config collectorConfig) (Collector, error) {
39return &PGStatStatementsCollector{log: config.logger}, nil
40}
41
42var (
43statSTatementsCallsTotal = prometheus.NewDesc(
44prometheus.BuildFQName(namespace, statStatementsSubsystem, "calls_total"),
45"Number of times executed",
46[]string{"user", "datname", "queryid"},
47prometheus.Labels{},
48)
49statStatementsSecondsTotal = prometheus.NewDesc(
50prometheus.BuildFQName(namespace, statStatementsSubsystem, "seconds_total"),
51"Total time spent in the statement, in seconds",
52[]string{"user", "datname", "queryid"},
53prometheus.Labels{},
54)
55statStatementsRowsTotal = prometheus.NewDesc(
56prometheus.BuildFQName(namespace, statStatementsSubsystem, "rows_total"),
57"Total number of rows retrieved or affected by the statement",
58[]string{"user", "datname", "queryid"},
59prometheus.Labels{},
60)
61statStatementsBlockReadSecondsTotal = prometheus.NewDesc(
62prometheus.BuildFQName(namespace, statStatementsSubsystem, "block_read_seconds_total"),
63"Total time the statement spent reading blocks, in seconds",
64[]string{"user", "datname", "queryid"},
65prometheus.Labels{},
66)
67statStatementsBlockWriteSecondsTotal = prometheus.NewDesc(
68prometheus.BuildFQName(namespace, statStatementsSubsystem, "block_write_seconds_total"),
69"Total time the statement spent writing blocks, in seconds",
70[]string{"user", "datname", "queryid"},
71prometheus.Labels{},
72)
73
74pgStatStatementsQuery = `SELECT
75pg_get_userbyid(userid) as user,
76pg_database.datname,
77pg_stat_statements.queryid,
78pg_stat_statements.calls as calls_total,
79pg_stat_statements.total_time / 1000.0 as seconds_total,
80pg_stat_statements.rows as rows_total,
81pg_stat_statements.blk_read_time / 1000.0 as block_read_seconds_total,
82pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total
83FROM pg_stat_statements
84JOIN pg_database
85ON pg_database.oid = pg_stat_statements.dbid
86WHERE
87total_time > (
88SELECT percentile_cont(0.1)
89WITHIN GROUP (ORDER BY total_time)
90FROM pg_stat_statements
91)
92ORDER BY seconds_total DESC
93LIMIT 100;`
94
95pgStatStatementsNewQuery = `SELECT
96pg_get_userbyid(userid) as user,
97pg_database.datname,
98pg_stat_statements.queryid,
99pg_stat_statements.calls as calls_total,
100pg_stat_statements.total_exec_time / 1000.0 as seconds_total,
101pg_stat_statements.rows as rows_total,
102pg_stat_statements.blk_read_time / 1000.0 as block_read_seconds_total,
103pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total
104FROM pg_stat_statements
105JOIN pg_database
106ON pg_database.oid = pg_stat_statements.dbid
107WHERE
108total_exec_time > (
109SELECT percentile_cont(0.1)
110WITHIN GROUP (ORDER BY total_exec_time)
111FROM pg_stat_statements
112)
113ORDER BY seconds_total DESC
114LIMIT 100;`
115)
116
117func (PGStatStatementsCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
118query := pgStatStatementsQuery
119if instance.version.GE(semver.MustParse("13.0.0")) {
120query = pgStatStatementsNewQuery
121}
122
123db := instance.getDB()
124rows, err := db.QueryContext(ctx, query)
125
126if err != nil {
127return err
128}
129defer rows.Close()
130for rows.Next() {
131var user, datname, queryid sql.NullString
132var callsTotal, rowsTotal sql.NullInt64
133var secondsTotal, blockReadSecondsTotal, blockWriteSecondsTotal sql.NullFloat64
134
135if err := rows.Scan(&user, &datname, &queryid, &callsTotal, &secondsTotal, &rowsTotal, &blockReadSecondsTotal, &blockWriteSecondsTotal); err != nil {
136return err
137}
138
139userLabel := "unknown"
140if user.Valid {
141userLabel = user.String
142}
143datnameLabel := "unknown"
144if datname.Valid {
145datnameLabel = datname.String
146}
147queryidLabel := "unknown"
148if queryid.Valid {
149queryidLabel = queryid.String
150}
151
152callsTotalMetric := 0.0
153if callsTotal.Valid {
154callsTotalMetric = float64(callsTotal.Int64)
155}
156ch <- prometheus.MustNewConstMetric(
157statSTatementsCallsTotal,
158prometheus.CounterValue,
159callsTotalMetric,
160userLabel, datnameLabel, queryidLabel,
161)
162
163secondsTotalMetric := 0.0
164if secondsTotal.Valid {
165secondsTotalMetric = secondsTotal.Float64
166}
167ch <- prometheus.MustNewConstMetric(
168statStatementsSecondsTotal,
169prometheus.CounterValue,
170secondsTotalMetric,
171userLabel, datnameLabel, queryidLabel,
172)
173
174rowsTotalMetric := 0.0
175if rowsTotal.Valid {
176rowsTotalMetric = float64(rowsTotal.Int64)
177}
178ch <- prometheus.MustNewConstMetric(
179statStatementsRowsTotal,
180prometheus.CounterValue,
181rowsTotalMetric,
182userLabel, datnameLabel, queryidLabel,
183)
184
185blockReadSecondsTotalMetric := 0.0
186if blockReadSecondsTotal.Valid {
187blockReadSecondsTotalMetric = blockReadSecondsTotal.Float64
188}
189ch <- prometheus.MustNewConstMetric(
190statStatementsBlockReadSecondsTotal,
191prometheus.CounterValue,
192blockReadSecondsTotalMetric,
193userLabel, datnameLabel, queryidLabel,
194)
195
196blockWriteSecondsTotalMetric := 0.0
197if blockWriteSecondsTotal.Valid {
198blockWriteSecondsTotalMetric = blockWriteSecondsTotal.Float64
199}
200ch <- prometheus.MustNewConstMetric(
201statStatementsBlockWriteSecondsTotal,
202prometheus.CounterValue,
203blockWriteSecondsTotalMetric,
204userLabel, datnameLabel, queryidLabel,
205)
206}
207if err := rows.Err(); err != nil {
208return err
209}
210return nil
211}
212