Skip to content

Commit 0cfc7b2

Browse files
committed
feat: allow excluding databases or users names in stat_statements
Add a couple of of options to the `stat_statements` collector that allow excluding certain database names or user names from metrics. This is useful if you don't care about certain preconfigured databases with admin activity. Signed-off-by: Cristian Greco <cristian@regolo.cc>
1 parent 7786601 commit 0cfc7b2

File tree

3 files changed

+333
-17
lines changed

3 files changed

+333
-17
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ This will build the docker image as `prometheuscommunity/postgres_exporter:${bra
156156
* `--collector.stat_statements.query_length`
157157
Maximum length of the statement text. Default is 120.
158158

159+
* `--collector.stat_statements.limit`
160+
Maximum number of statements to return. Default is 100.
161+
162+
* `--collector.stat_statements.exclude_databases`
163+
Comma-separated list of database names to exclude from `pg_stat_statements` metrics. Default is none.
164+
165+
* `--collector.stat_statements.exclude_users`
166+
Comma-separated list of user names to exclude from `pg_stat_statements` metrics. Default is none.
167+
159168
* `[no-]collector.stat_user_tables`
160169
Enable the `stat_user_tables` collector (default: enabled).
161170

collector/pg_stat_statements.go

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"database/sql"
1919
"fmt"
2020
"log/slog"
21+
"strings"
2122

2223
"github.com/alecthomas/kingpin/v2"
2324
"github.com/blang/semver/v4"
@@ -30,9 +31,11 @@ const (
3031
)
3132

3233
var (
33-
includeQueryFlag *bool = nil
34-
statementLengthFlag *uint = nil
35-
statementLimitFlag *uint = nil
34+
includeQueryFlag *bool = nil
35+
statementLengthFlag *uint = nil
36+
statementLimitFlag *uint = nil
37+
excludedDatabasesFlag *string = nil
38+
excludedUsersFlag *string = nil
3639
)
3740

3841
func init() {
@@ -56,21 +59,53 @@ func init() {
5659
"Maximum number of statements to return.").
5760
Default(defaultStatementLimit).
5861
Uint()
62+
excludedDatabasesFlag = kingpin.Flag(
63+
fmt.Sprint(collectorFlagPrefix, statStatementsSubsystem, ".exclude_databases"),
64+
"Comma-separated list of database names to exclude. (default: none)").
65+
Default("").
66+
String()
67+
excludedUsersFlag = kingpin.Flag(
68+
fmt.Sprint(collectorFlagPrefix, statStatementsSubsystem, ".exclude_users"),
69+
"Comma-separated list of user names to exclude. (default: none)").
70+
Default("").
71+
String()
5972
}
6073

6174
type PGStatStatementsCollector struct {
6275
log *slog.Logger
6376
includeQueryStatement bool
6477
statementLength uint
6578
statementLimit uint
79+
excludedDatabases []string
80+
excludedUsers []string
6681
}
6782

6883
func NewPGStatStatementsCollector(config collectorConfig) (Collector, error) {
84+
var excludedDatabases []string
85+
if *excludedDatabasesFlag != "" {
86+
for db := range strings.SplitSeq(*excludedDatabasesFlag, ",") {
87+
if trimmed := strings.TrimSpace(db); trimmed != "" {
88+
excludedDatabases = append(excludedDatabases, trimmed)
89+
}
90+
}
91+
}
92+
93+
var excludedUsers []string
94+
if *excludedUsersFlag != "" {
95+
for user := range strings.SplitSeq(*excludedUsersFlag, ",") {
96+
if trimmed := strings.TrimSpace(user); trimmed != "" {
97+
excludedUsers = append(excludedUsers, trimmed)
98+
}
99+
}
100+
}
101+
69102
return &PGStatStatementsCollector{
70103
log: config.logger,
71104
includeQueryStatement: *includeQueryFlag,
72105
statementLength: *statementLengthFlag,
73106
statementLimit: *statementLimitFlag,
107+
excludedDatabases: excludedDatabases,
108+
excludedUsers: excludedUsers,
74109
}, nil
75110
}
76111

@@ -115,7 +150,9 @@ var (
115150
)
116151

117152
const (
118-
pgStatStatementQuerySelect = `LEFT(pg_stat_statements.query, %d) as query,`
153+
pgStatStatementQuerySelect = `LEFT(pg_stat_statements.query, %d) as query,`
154+
pgStatStatementExcludeDatabases = `AND pg_database.datname NOT IN (%s) `
155+
pgStatStatementExcludeUsers = `AND pg_get_userbyid(userid) NOT IN (%s) `
119156

120157
pgStatStatementsQuery = `SELECT
121158
pg_get_userbyid(userid) as user,
@@ -136,6 +173,7 @@ const (
136173
WITHIN GROUP (ORDER BY total_time)
137174
FROM pg_stat_statements
138175
)
176+
%s
139177
ORDER BY seconds_total DESC
140178
LIMIT %s;`
141179

@@ -158,6 +196,7 @@ const (
158196
WITHIN GROUP (ORDER BY total_exec_time)
159197
FROM pg_stat_statements
160198
)
199+
%s
161200
ORDER BY seconds_total DESC
162201
LIMIT %s;`
163202

@@ -180,6 +219,7 @@ const (
180219
WITHIN GROUP (ORDER BY total_exec_time)
181220
FROM pg_stat_statements
182221
)
222+
%s
183223
ORDER BY seconds_total DESC
184224
LIMIT %s;`
185225
)
@@ -198,11 +238,13 @@ func (c PGStatStatementsCollector) Update(ctx context.Context, instance *instanc
198238
if c.includeQueryStatement {
199239
querySelect = fmt.Sprintf(pgStatStatementQuerySelect, c.statementLength)
200240
}
241+
databaseFilter := c.buildExcludedDatabasesClause()
242+
userFilter := c.buildExcludedUsersClause()
201243
statementLimit := defaultStatementLimit
202244
if c.statementLimit > 0 {
203245
statementLimit = fmt.Sprintf("%d", c.statementLimit)
204246
}
205-
query := fmt.Sprintf(queryTemplate, querySelect, statementLimit)
247+
query := fmt.Sprintf(queryTemplate, querySelect, databaseFilter+userFilter, statementLimit)
206248

207249
db := instance.getDB()
208250
rows, err := db.QueryContext(ctx, query)
@@ -319,3 +361,28 @@ func (c PGStatStatementsCollector) Update(ctx context.Context, instance *instanc
319361
}
320362
return nil
321363
}
364+
365+
func (c PGStatStatementsCollector) buildExcludedDatabasesClause() string {
366+
if len(c.excludedDatabases) == 0 {
367+
return ""
368+
}
369+
370+
databases := make([]string, 0, len(c.excludedDatabases))
371+
for _, db := range c.excludedDatabases {
372+
databases = append(databases, fmt.Sprintf("'%s'", strings.ReplaceAll(db, "'", "''")))
373+
}
374+
375+
return fmt.Sprintf(pgStatStatementExcludeDatabases, strings.Join(databases, ", "))
376+
}
377+
378+
func (c PGStatStatementsCollector) buildExcludedUsersClause() string {
379+
if len(c.excludedUsers) == 0 {
380+
return ""
381+
}
382+
383+
users := make([]string, 0, len(c.excludedUsers))
384+
for _, user := range c.excludedUsers {
385+
users = append(users, fmt.Sprintf("'%s'", strings.ReplaceAll(user, "'", "''")))
386+
}
387+
return fmt.Sprintf(pgStatStatementExcludeUsers, strings.Join(users, ", "))
388+
}

0 commit comments

Comments
 (0)