pangolin_exporter
115 строк · 3.1 Кб
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/go-kit/log"
21"github.com/go-kit/log/level"
22"github.com/prometheus/client_golang/prometheus"
23)
24
25const databaseWraparoundSubsystem = "database_wraparound"
26
27func init() {
28registerCollector(databaseWraparoundSubsystem, defaultDisabled, NewPGDatabaseWraparoundCollector)
29}
30
31type PGDatabaseWraparoundCollector struct {
32log log.Logger
33}
34
35func NewPGDatabaseWraparoundCollector(config collectorConfig) (Collector, error) {
36return &PGDatabaseWraparoundCollector{log: config.logger}, nil
37}
38
39var (
40databaseWraparoundAgeDatfrozenxid = prometheus.NewDesc(
41prometheus.BuildFQName(namespace, databaseWraparoundSubsystem, "age_datfrozenxid_seconds"),
42"Age of the oldest transaction ID that has not been frozen.",
43[]string{"datname"},
44prometheus.Labels{},
45)
46databaseWraparoundAgeDatminmxid = prometheus.NewDesc(
47prometheus.BuildFQName(namespace, databaseWraparoundSubsystem, "age_datminmxid_seconds"),
48"Age of the oldest multi-transaction ID that has been replaced with a transaction ID.",
49[]string{"datname"},
50prometheus.Labels{},
51)
52
53databaseWraparoundQuery = `
54SELECT
55datname,
56age(d.datfrozenxid) as age_datfrozenxid,
57mxid_age(d.datminmxid) as age_datminmxid
58FROM
59pg_catalog.pg_database d
60WHERE
61d.datallowconn
62`
63)
64
65func (c *PGDatabaseWraparoundCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
66db := instance.getDB()
67rows, err := db.QueryContext(ctx,
68databaseWraparoundQuery)
69
70if err != nil {
71return err
72}
73defer rows.Close()
74
75for rows.Next() {
76var datname sql.NullString
77var ageDatfrozenxid, ageDatminmxid sql.NullFloat64
78
79if err := rows.Scan(&datname, &ageDatfrozenxid, &ageDatminmxid); err != nil {
80return err
81}
82
83if !datname.Valid {
84level.Debug(c.log).Log("msg", "Skipping database with NULL name")
85continue
86}
87if !ageDatfrozenxid.Valid {
88level.Debug(c.log).Log("msg", "Skipping stat emission with NULL age_datfrozenxid")
89continue
90}
91if !ageDatminmxid.Valid {
92level.Debug(c.log).Log("msg", "Skipping stat emission with NULL age_datminmxid")
93continue
94}
95
96ageDatfrozenxidMetric := ageDatfrozenxid.Float64
97
98ch <- prometheus.MustNewConstMetric(
99databaseWraparoundAgeDatfrozenxid,
100prometheus.GaugeValue,
101ageDatfrozenxidMetric, datname.String,
102)
103
104ageDatminmxidMetric := ageDatminmxid.Float64
105ch <- prometheus.MustNewConstMetric(
106databaseWraparoundAgeDatminmxid,
107prometheus.GaugeValue,
108ageDatminmxidMetric, datname.String,
109)
110}
111if err := rows.Err(); err != nil {
112return err
113}
114return nil
115}
116