pangolin_exporter
382 строки · 16.6 Кб
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 statementsSubsystem = "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(statementsSubsystem, defaultEnabled, NewPGstatementsCollector)
32}
33
34type PGstatementsCollector struct {
35log log.Logger
36}
37
38func NewPGstatementsCollector(config collectorConfig) (Collector, error) {
39return &PGstatementsCollector{log: config.logger}, nil
40}
41
42var (
43statSTatementsQueryInfo = prometheus.NewDesc(
44prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "query_info"),
45"Labeled info about statements has been executed.",
46[]string{"user", "database", "queryid", "query"},
47prometheus.Labels{},
48)
49statSTatementsCalls = prometheus.NewDesc(
50prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "calls_total"),
51"Total number of times statement has been executed.",
52[]string{"user", "database", "queryid"},
53prometheus.Labels{},
54)
55statSTatementsRowsTotal = prometheus.NewDesc(
56prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "rows_total"),
57"Total number of rows retrieved or affected by the statement.",
58[]string{"user", "database", "queryid"},
59prometheus.Labels{},
60)
61statSTatementsTimes = prometheus.NewDesc(
62prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "time_seconds_total"),
63"Time spent by the statement in each mode, in seconds.",
64[]string{"user", "database", "queryid", "mode"},
65prometheus.Labels{},
66)
67statSTatementsAllTimes = prometheus.NewDesc(
68prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "time_seconds_all_total"),
69"Total time spent by the statement, in seconds.",
70[]string{"user", "database", "queryid"},
71prometheus.Labels{},
72)
73statSTatementsSharedBuffersHitTotal = prometheus.NewDesc(
74prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "shared_buffers_hit_total"),
75"Total number of blocks have been found in shared buffers by the statement.",
76[]string{"user", "database", "queryid"},
77prometheus.Labels{},
78)
79statSTatementSharedBuffersReadBytesTotal = prometheus.NewDesc(
80prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "shared_buffers_read_bytes_total"),
81"Total number of bytes read from disk or OS page cache by the statement when block not found in shared buffers.",
82[]string{"user", "database", "queryid"},
83prometheus.Labels{},
84)
85statSTatementsSharedBuffersDirtiedTotal = prometheus.NewDesc(
86prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "shared_buffers_dirtied_total"),
87"Total number of blocks have been dirtied in shared buffers by the statement.",
88[]string{"user", "database", "queryid"},
89prometheus.Labels{},
90)
91statSTatementsSharedBuffersWrittenBytesTotal = prometheus.NewDesc(
92prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "shared_buffers_written_bytes_total"),
93"Total number of bytes written from shared buffers to disk by the statement.",
94[]string{"user", "database", "queryid"},
95prometheus.Labels{},
96)
97statSTatementsLocalBuffersHitTotal = prometheus.NewDesc(
98prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "local_buffers_hit_total"),
99"Total number of blocks have been found in local buffers by the statement.",
100[]string{"user", "database", "queryid"},
101prometheus.Labels{},
102)
103statSTatementsLocalBuffersReadBytesTotal = prometheus.NewDesc(
104prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "local_buffers_read_bytes_total"),
105"Total number of bytes read from disk or OS page cache by the statement when block not found in local buffers.",
106[]string{"user", "database", "queryid"},
107prometheus.Labels{},
108)
109statSTatementsLocalBuffersDirtiedTotal = prometheus.NewDesc(
110prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "local_buffers_dirtied_total"),
111"Total number of blocks have been dirtied in local buffers by the statement.",
112[]string{"user", "database", "queryid"},
113prometheus.Labels{},
114)
115statSTatementsLocalBuffersWrittenBytesTotal = prometheus.NewDesc(
116prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "local_buffers_written_bytes_total"),
117"Total number of bytes written from local buffers to disk by the statement.",
118[]string{"user", "database", "queryid"},
119prometheus.Labels{},
120)
121statSTatementsTempReadBytesTotal = prometheus.NewDesc(
122prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "temp_read_bytes_total"),
123"Total number of bytes read from temporary files by the statement.",
124[]string{"user", "database", "queryid"},
125prometheus.Labels{},
126)
127statSTatementsTempWrittenBytesTotal = prometheus.NewDesc(
128prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "temp_written_bytes_total"),
129"Total number of bytes written to temporary files by the statement.",
130[]string{"user", "database", "queryid"},
131prometheus.Labels{},
132)
133statSTatementsWalRecordsTotal = prometheus.NewDesc(
134prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "wal_records_total"),
135"Total number of WAL records generated by the statement.",
136[]string{"user", "database", "queryid"},
137prometheus.Labels{},
138)
139
140statSTatementsWalBytesAllTotal = prometheus.NewDesc(
141prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "wal_bytes_all_total"),
142"Total number of WAL generated by the statement, in bytes.",
143[]string{"user", "database", "queryid"},
144prometheus.Labels{},
145)
146statSTatementsWalWalBytesTotal = prometheus.NewDesc(
147prometheus.BuildFQName(namespace_pangolin, statementsSubsystem, "wal_bytes_total"),
148"Total number of WAL bytes generated by the statement, by type.",
149[]string{"user", "database", "queryid", "wal"},
150prometheus.Labels{},
151)
152
153// postgresStatementsQuery12 defines query for querying statements metrics for PG12 and older.
154postgresStatementsQuery12 = "SELECT d.datname AS database, pg_get_userbyid(p.userid) AS user, p.queryid, " +
155"p.query, p.calls, p.rows, p.total_time, p.blk_read_time, p.blk_write_time, " +
156"nullif(p.shared_blks_hit, 0) AS shared_blks_hit, nullif(p.shared_blks_read, 0) AS shared_blks_read, " +
157"nullif(p.shared_blks_dirtied, 0) AS shared_blks_dirtied, nullif(p.shared_blks_written, 0) AS shared_blks_written, " +
158"nullif(p.local_blks_hit, 0) AS local_blks_hit, nullif(p.local_blks_read, 0) AS local_blks_read, " +
159"nullif(p.local_blks_dirtied, 0) AS local_blks_dirtied, nullif(p.local_blks_written, 0) AS local_blks_written, " +
160"nullif(p.temp_blks_read, 0) AS temp_blks_read, nullif(p.temp_blks_written, 0) AS temp_blks_written " +
161"FROM pg_stat_statements p JOIN pg_database d ON d.oid=p.dbid;"
162
163// postgresStatementsQueryLatest defines query for querying statements metrics.
164// 1. use nullif(value, 0) to nullify zero values, NULL are skipped by stats method and metrics wil not be generated.
165postgresStatementsQueryLatest = `SELECT d.datname AS database, pg_get_userbyid(p.userid) AS user, p.queryid,
166p.query, p.calls, p.rows, p.total_exec_time, p.total_plan_time, p.blk_read_time, p.blk_write_time,
167nullif(p.shared_blks_hit, 0) AS shared_blks_hit, nullif(p.shared_blks_read, 0) AS shared_blks_read,
168nullif(p.shared_blks_dirtied, 0) AS shared_blks_dirtied, nullif(p.shared_blks_written, 0) AS shared_blks_written,
169nullif(p.local_blks_hit, 0) AS local_blks_hit, nullif(p.local_blks_read, 0) AS local_blks_read,
170nullif(p.local_blks_dirtied, 0) AS local_blks_dirtied, nullif(p.local_blks_written, 0) AS local_blks_written,
171nullif(p.temp_blks_read, 0) AS temp_blks_read, nullif(p.temp_blks_written, 0) AS temp_blks_written,
172nullif(p.wal_records, 0) AS wal_records, nullif(p.wal_fpi, 0) AS wal_fpi, nullif(p.wal_bytes, 0) AS wal_bytes
173FROM pg_stat_statements p JOIN pg_database d ON d.oid=p.dbid;`
174)
175
176func (c PGstatementsCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
177db := instance.getDB()
178
179query := postgresStatementsQuery12
180if instance.version.GE(semver.MustParse("13.0.0")) {
181query = postgresStatementsQueryLatest
182}
183
184rows1 := db.QueryRow(`SELECT setting FROM pg_settings WHERE name = 'block_size';`)
185var bsize sql.NullFloat64
186rows1.Scan(&bsize)
187blockSize := 0.0
188if bsize.Valid {
189blockSize = float64(bsize.Float64)
190}
191
192rows, err := db.QueryContext(ctx, query)
193if err != nil {
194return err
195}
196defer rows.Close()
197for rows.Next() {
198
199var database, user, queryid, query sql.NullString
200var calls, rows_, totalExecTime, totalPlanTime, blkReadTime, blkWriteTime, sharedBlksHit, sharedBlksRead sql.NullFloat64
201var sharedBlksDirtied, sharedBlksWritten, localBlksHit, localBlksRead, localBlksDirtied, localBlksWritten sql.NullFloat64
202var tempBlksRead, tempBlksWritten, walRecords, walFPI, walBytes sql.NullFloat64
203
204if err := rows.Scan(&database, &user, &queryid, &query, &calls, &rows_, &totalExecTime, &totalPlanTime, &blkReadTime, &blkWriteTime, &sharedBlksHit, &sharedBlksRead,
205&sharedBlksDirtied, &sharedBlksWritten, &localBlksHit, &localBlksRead, &localBlksDirtied, &localBlksWritten, &tempBlksRead, &tempBlksWritten, &walRecords,
206&walFPI, &walBytes); err != nil {
207return err
208}
209databaseLabel := "unknown"
210if database.Valid {
211databaseLabel = database.String
212}
213userLabel := "unknown"
214if user.Valid {
215userLabel = user.String
216}
217queryidLabel := "unknown"
218if queryid.Valid {
219queryidLabel = queryid.String
220}
221queryLabel := "unknown"
222if query.Valid {
223queryLabel = query.String
224}
225
226QueryInfoMetric := 1.0
227ch <- prometheus.MustNewConstMetric(statSTatementsQueryInfo, prometheus.CounterValue, QueryInfoMetric, databaseLabel, userLabel, queryidLabel, queryLabel)
228
229callsMetric := 0.0
230if calls.Valid {
231callsMetric = float64(calls.Float64)
232}
233ch <- prometheus.MustNewConstMetric(statSTatementsCalls, prometheus.CounterValue, callsMetric, databaseLabel, userLabel, queryidLabel)
234
235rowsMetric := 0.0
236if rows_.Valid {
237rowsMetric = float64(rows_.Float64)
238}
239ch <- prometheus.MustNewConstMetric(statSTatementsRowsTotal, prometheus.CounterValue, rowsMetric, databaseLabel, userLabel, queryidLabel)
240
241// total = planning + execution; execution already includes io time.
242totalExecTimeMetric := 0.0
243if totalExecTime.Valid {
244if totalPlanTime.Valid {
245totalExecTimeMetric = float64(totalPlanTime.Float64) + float64(totalExecTime.Float64)
246}
247}
248ch <- prometheus.MustNewConstMetric(statSTatementsAllTimes, prometheus.CounterValue, totalExecTimeMetric, databaseLabel, userLabel, queryidLabel)
249
250totalPlanTimeMetric := 0.0
251if totalPlanTime.Valid {
252totalPlanTimeMetric = float64(totalPlanTime.Float64)
253}
254
255ch <- prometheus.MustNewConstMetric(statSTatementsTimes, prometheus.CounterValue, totalPlanTimeMetric, databaseLabel, userLabel, queryidLabel, "planning")
256// execution time = execution - io times.
257executingeMetric := 0.0
258
259if totalExecTime.Valid {
260if blkReadTime.Valid {
261if blkWriteTime.Valid {
262executingeMetric = float64(totalExecTime.Float64) - (float64(blkReadTime.Float64) + float64(blkWriteTime.Float64))
263}
264}
265}
266ch <- prometheus.MustNewConstMetric(statSTatementsTimes, prometheus.CounterValue, executingeMetric, databaseLabel, userLabel, queryidLabel, "executing")
267
268// avoid metrics spamming and send metrics only if they greater than zero.
269blkReadTimeMetric := 0.0
270if blkReadTime.Valid {
271blkReadTimeMetric = float64(blkReadTime.Float64)
272}
273if blkReadTimeMetric > 0 {
274ch <- prometheus.MustNewConstMetric(statSTatementsTimes, prometheus.CounterValue, blkReadTimeMetric, databaseLabel, userLabel, queryidLabel, "ioread")
275}
276blkWriteTimeMetric := 0.0
277if blkWriteTime.Valid {
278blkWriteTimeMetric = float64(blkWriteTime.Float64)
279}
280if blkWriteTimeMetric > 0 {
281ch <- prometheus.MustNewConstMetric(statSTatementsTimes, prometheus.CounterValue, blkWriteTimeMetric, databaseLabel, userLabel, queryidLabel, "iowrite")
282}
283sharedBlksHitMetric := 0.0
284if sharedBlksHit.Valid {
285sharedBlksHitMetric = float64(sharedBlksHit.Float64)
286}
287if sharedBlksHitMetric > 0 {
288ch <- prometheus.MustNewConstMetric(statSTatementsSharedBuffersHitTotal, prometheus.CounterValue, sharedBlksHitMetric, databaseLabel, userLabel, queryidLabel)
289}
290sharedBlksReadMetric := 0.0
291if sharedBlksRead.Valid {
292sharedBlksReadMetric = float64(sharedBlksRead.Float64)
293}
294if sharedBlksReadMetric > 0 {
295ch <- prometheus.MustNewConstMetric(statSTatementSharedBuffersReadBytesTotal, prometheus.CounterValue, sharedBlksReadMetric, databaseLabel, userLabel, queryidLabel)
296}
297sharedBlksDirtiedMetric := 0.0
298if sharedBlksDirtied.Valid {
299sharedBlksDirtiedMetric = float64(sharedBlksDirtied.Float64)
300}
301if sharedBlksDirtiedMetric > 0 {
302ch <- prometheus.MustNewConstMetric(statSTatementsSharedBuffersDirtiedTotal, prometheus.CounterValue, sharedBlksDirtiedMetric, databaseLabel, userLabel, queryidLabel)
303}
304sharedBlksWrittenMetric := 0.0
305if sharedBlksWritten.Valid {
306sharedBlksWrittenMetric = float64(sharedBlksWritten.Float64)
307}
308if sharedBlksWrittenMetric > 0 {
309ch <- prometheus.MustNewConstMetric(statSTatementsSharedBuffersWrittenBytesTotal, prometheus.CounterValue, sharedBlksWrittenMetric, databaseLabel, userLabel, queryidLabel)
310}
311
312localBlksHitMetric := 0.0
313if localBlksHit.Valid {
314localBlksHitMetric = float64(localBlksHit.Float64)
315}
316if localBlksHitMetric > 0 {
317ch <- prometheus.MustNewConstMetric(statSTatementsLocalBuffersHitTotal, prometheus.CounterValue, localBlksHitMetric, databaseLabel, userLabel, queryidLabel)
318}
319localBlksReadMetric := 0.0
320if localBlksRead.Valid {
321localBlksReadMetric = float64(localBlksRead.Float64)
322}
323if localBlksReadMetric > 0 {
324ch <- prometheus.MustNewConstMetric(statSTatementsLocalBuffersReadBytesTotal, prometheus.CounterValue, localBlksReadMetric, databaseLabel, userLabel, queryidLabel)
325}
326localBlksDirtiedMetric := 0.0
327if localBlksDirtied.Valid {
328localBlksDirtiedMetric = float64(localBlksDirtied.Float64)
329}
330if localBlksDirtiedMetric > 0 {
331ch <- prometheus.MustNewConstMetric(statSTatementsLocalBuffersDirtiedTotal, prometheus.CounterValue, localBlksDirtiedMetric, databaseLabel, userLabel, queryidLabel)
332}
333localBlksWrittenMetric := 0.0
334if localBlksWritten.Valid {
335localBlksWrittenMetric = float64(localBlksWritten.Float64)
336}
337if localBlksReadMetric > 0 {
338ch <- prometheus.MustNewConstMetric(statSTatementsLocalBuffersWrittenBytesTotal, prometheus.CounterValue, localBlksWrittenMetric, databaseLabel, userLabel, queryidLabel)
339}
340tempBlksReadMetric := 0.0
341if tempBlksRead.Valid {
342tempBlksReadMetric = float64(tempBlksRead.Float64)
343}
344if tempBlksReadMetric > 0 {
345ch <- prometheus.MustNewConstMetric(statSTatementsTempReadBytesTotal, prometheus.CounterValue, tempBlksReadMetric, databaseLabel, userLabel, queryidLabel)
346}
347tempBlksWrittenMetric := 0.0
348if tempBlksWritten.Valid {
349tempBlksWrittenMetric = float64(tempBlksWritten.Float64)
350}
351if tempBlksWrittenMetric > 0 {
352ch <- prometheus.MustNewConstMetric(statSTatementsTempWrittenBytesTotal, prometheus.CounterValue, tempBlksWrittenMetric, databaseLabel, userLabel, queryidLabel)
353}
354
355walRecordsMetric := 0.0
356if walRecords.Valid {
357walRecordsMetric = float64(walRecords.Float64)
358}
359if walRecordsMetric > 0 {
360// WAL records
361ch <- prometheus.MustNewConstMetric(statSTatementsWalRecordsTotal, prometheus.CounterValue, walRecordsMetric, databaseLabel, userLabel, queryidLabel)
362// WAL total bytes
363if walFPI.Valid {
364if walBytes.Valid {
365walAllBytesMetric := (float64(walFPI.Float64) * blockSize) + float64(walBytes.Float64)
366ch <- prometheus.MustNewConstMetric(statSTatementsWalBytesAllTotal, prometheus.CounterValue, walAllBytesMetric, databaseLabel, userLabel, queryidLabel)
367walBytesMetric := (float64(walFPI.Float64) * blockSize)
368ch <- prometheus.MustNewConstMetric(statSTatementsWalWalBytesTotal, prometheus.CounterValue, walBytesMetric, databaseLabel, userLabel, queryidLabel, "fpi")
369ch <- prometheus.MustNewConstMetric(statSTatementsWalWalBytesTotal, prometheus.CounterValue, float64(walFPI.Float64), databaseLabel, userLabel, queryidLabel, "regular")
370
371}
372}
373
374}
375
376}
377if err := rows.Err(); err != nil {
378return err
379}
380return nil
381
382}
383