pangolin_exporter
132 строки · 3.5 Кб
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/lib/pq"
22"github.com/prometheus/client_golang/prometheus"
23)
24
25func init() {
26// Making this default disabled because we have no tests for it
27registerCollector(processIdleSubsystem, defaultDisabled, NewPGProcessIdleCollector)
28}
29
30type PGProcessIdleCollector struct {
31log log.Logger
32}
33
34const processIdleSubsystem = "process_idle"
35
36func NewPGProcessIdleCollector(config collectorConfig) (Collector, error) {
37return &PGProcessIdleCollector{log: config.logger}, nil
38}
39
40var pgProcessIdleSeconds = prometheus.NewDesc(
41prometheus.BuildFQName(namespace, processIdleSubsystem, "seconds"),
42"Idle time of server processes",
43[]string{"state", "application_name"},
44prometheus.Labels{},
45)
46
47func (PGProcessIdleCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
48db := instance.getDB()
49row := db.QueryRowContext(ctx,
50`WITH
51metrics AS (
52SELECT
53state,
54application_name,
55SUM(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - state_change))::bigint)::float AS process_idle_seconds_sum,
56COUNT(*) AS process_idle_seconds_count
57FROM pg_stat_activity
58WHERE state ~ '^idle'
59GROUP BY state, application_name
60),
61buckets AS (
62SELECT
63state,
64application_name,
65le,
66SUM(
67CASE WHEN EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - state_change)) <= le
68THEN 1
69ELSE 0
70END
71)::bigint AS bucket
72FROM
73pg_stat_activity,
74UNNEST(ARRAY[1, 2, 5, 15, 30, 60, 90, 120, 300]) AS le
75GROUP BY state, application_name, le
76ORDER BY state, application_name, le
77)
78SELECT
79state,
80application_name,
81process_idle_seconds_sum as seconds_sum,
82process_idle_seconds_count as seconds_count,
83ARRAY_AGG(le) AS seconds,
84ARRAY_AGG(bucket) AS seconds_bucket
85FROM metrics JOIN buckets USING (state, application_name)
86GROUP BY 1, 2, 3, 4;`)
87
88var state sql.NullString
89var applicationName sql.NullString
90var secondsSum sql.NullFloat64
91var secondsCount sql.NullInt64
92var seconds []float64
93var secondsBucket []int64
94
95err := row.Scan(&state, &applicationName, &secondsSum, &secondsCount, pq.Array(&seconds), pq.Array(&secondsBucket))
96if err != nil {
97return err
98}
99
100var buckets = make(map[float64]uint64, len(seconds))
101for i, second := range seconds {
102if i >= len(secondsBucket) {
103break
104}
105buckets[second] = uint64(secondsBucket[i])
106}
107
108stateLabel := "unknown"
109if state.Valid {
110stateLabel = state.String
111}
112
113applicationNameLabel := "unknown"
114if applicationName.Valid {
115applicationNameLabel = applicationName.String
116}
117
118var secondsCountMetric uint64
119if secondsCount.Valid {
120secondsCountMetric = uint64(secondsCount.Int64)
121}
122secondsSumMetric := 0.0
123if secondsSum.Valid {
124secondsSumMetric = secondsSum.Float64
125}
126ch <- prometheus.MustNewConstHistogram(
127pgProcessIdleSeconds,
128secondsCountMetric, secondsSumMetric, buckets,
129stateLabel, applicationNameLabel,
130)
131return nil
132}
133