pg_metrics 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.markdown +90 -0
- data/DEV.markdown +22 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +22 -0
- data/README.markdown +43 -0
- data/bin/pg_metrics_statsd +14 -0
- data/lib/pg_metrics/metrics.rb +347 -0
- data/lib/pg_metrics/pgbouncer_metrics.rb +119 -0
- data/lib/pg_metrics/statsd.rb +85 -0
- data/lib/pg_metrics/version.rb +3 -0
- data/lib/pg_metrics.rb +7 -0
- data/pg_metrics.gemspec +38 -0
- data/rakefile +8 -0
- data/test/helper.rb +11 -0
- data/test/test_statsd.rb +95 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1f02f742e23e3d02686a15c2cf6a9f8b48b527e3
|
4
|
+
data.tar.gz: cb0e25e46b852b7cc3f3551b3c87d63bbf187acb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 615b4a325678b94b2f3596f924c8a5ac978314d2a4b0a18d09eb6785d45882545291948298b2ba849f20efd4b4e075cda241d7b091584a5ac1f60c656296f7df
|
7
|
+
data.tar.gz: 455211aeb1ef3fa4883edbee01bc70d9111433dfe9e04950134765097a8f7664d131b31e1f6b26e3e24a46e3422e6146a85ffc482bdf01c2481de26a8b9dc236
|
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# pg_metrics Changelog
|
2
|
+
|
3
|
+
## Changes between 0.1.1 and 0.0.7
|
4
|
+
|
5
|
+
### Remove sensu
|
6
|
+
|
7
|
+
pg_metrics generates too many stats for sensu to cope with, which
|
8
|
+
is why we made the statsd version in the first place. As we're not
|
9
|
+
using or testing the sensu version, remove it rather than let it bitrot.
|
10
|
+
|
11
|
+
### Update project homepage
|
12
|
+
|
13
|
+
Make the project public (yay!), making github.com the official homepage.
|
14
|
+
|
15
|
+
## Changes between 0.0.7 and 0.0.6
|
16
|
+
|
17
|
+
### Use client connection user in backend stats
|
18
|
+
|
19
|
+
In 0.0.5, backend-stats for automatic connections or non-force-user database
|
20
|
+
definitions used `SAMEUSER` as the username for the backend connection. The
|
21
|
+
actual client connection user is now used instead.
|
22
|
+
|
23
|
+
### Drop backend stats for pools with no corresponding backend
|
24
|
+
|
25
|
+
`pgbouncer` sometimes shows databases in `SHOW pools` that have no
|
26
|
+
corresponding entry in `SHOW databases`. These pool entries are
|
27
|
+
now ignored for backend stats.
|
28
|
+
|
29
|
+
## Changes between 0.0.5 and 0.0.6
|
30
|
+
|
31
|
+
### Add pgbouncer stats
|
32
|
+
|
33
|
+
`pg_metrics` can now be used to collect pgbouncer stats using the `--pgbouncer`
|
34
|
+
flag. Only results of `SHOW stats` and `SHOW pools` are collected, as well
|
35
|
+
as per-backend stats, which uses the results of `SHOW databases` to aggregate
|
36
|
+
pool stats per backend-connection, not just per defined database connection.
|
37
|
+
|
38
|
+
## Changes between 0.0.4 and 0.0.5
|
39
|
+
|
40
|
+
### Collect waiting session stats
|
41
|
+
|
42
|
+
Count sessions as *waiting* if `pg_stat_activity.waiting` is `TRUE`,
|
43
|
+
and as the session state otherwise (using `pg_stat_activity.state` for
|
44
|
+
PostgreSQL versions >= 9.2 and calculated from `pg_stat_activity.current_query`
|
45
|
+
in earlier versions).
|
46
|
+
|
47
|
+
`pg_stat_activity` does track waiting idependently of state, but for practical
|
48
|
+
purposes not all that useful to track them separately for metrics collection.
|
49
|
+
|
50
|
+
### Track xlog.location for PostgreSQL versions <= 9.0
|
51
|
+
|
52
|
+
Earlier versions of pg_metrics did not collect `current_xlog_location`
|
53
|
+
for versions earlier than 9.1. The `pg_current_xlog_location()` function
|
54
|
+
is available in both PostgreSQL 8.3 and 8.4. The `pg_last_xlog_(receive|replay)_location()`
|
55
|
+
functions are available for PostgreSQL versions >= 9.0, so collect those
|
56
|
+
when available.
|
57
|
+
|
58
|
+
|
59
|
+
## Changes between 0.0.3 and 0.0.4
|
60
|
+
|
61
|
+
### Allow specfication of which database stats are collected.
|
62
|
+
|
63
|
+
Prior to 0.0.4, per-database stats were collected only from `pg_locks`,
|
64
|
+
`pg_stat_user_tables` and `pg_statio_user_tables`. `pg_metrics_statsd`
|
65
|
+
collects all stats by default, and allows specification of which stats
|
66
|
+
to omit with a a variety of `--no-*` command line flags.
|
67
|
+
|
68
|
+
## Changes between 0.0.2 and 0.0.3
|
69
|
+
|
70
|
+
### Improve formatting of verbose output
|
71
|
+
|
72
|
+
0.0.3 prints each metric on its own line.
|
73
|
+
|
74
|
+
### Permit using short -s flag to specify scheme
|
75
|
+
|
76
|
+
0.0.3 allows you to specify scheme using `-s SCHEME` as well
|
77
|
+
as the legacy `--scheme SCHEME`.
|
78
|
+
|
79
|
+
## Changes between 0.0.1 and 0.0.2
|
80
|
+
|
81
|
+
### Fix use of regexp filter
|
82
|
+
|
83
|
+
A incomplete refactor left behind a second instantiation of the filter regex,
|
84
|
+
along with a reference to a variable that was no longer in scope.
|
85
|
+
|
86
|
+
## Set application_name only for PostgreSQL versions >= 9.0
|
87
|
+
|
88
|
+
The application_name parameter was introduced in PostgreSQL version 9.0. Earlier
|
89
|
+
versions (such as 8.3 and 8.4) will throw an error if you try to set it, so we
|
90
|
+
no longer try to set it for versions that don't support it.
|
data/DEV.markdown
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Development notes
|
2
|
+
|
3
|
+
Processes I often forget between release cycles
|
4
|
+
|
5
|
+
## To build and install locally
|
6
|
+
|
7
|
+
gem build pg_metrics.gemspec
|
8
|
+
gem install ./pg_metrics-0.0.X.gem
|
9
|
+
|
10
|
+
|
11
|
+
## To release
|
12
|
+
|
13
|
+
* Update `CHANGELOG.markdown`
|
14
|
+
* Update `lib/pg_metrics/version.rb`
|
15
|
+
* Update `spec.date` in `pg_metrics.gemspec`
|
16
|
+
* Tag
|
17
|
+
|
18
|
+
git tag -a "v0.0.X" -m "version 0.0.X"
|
19
|
+
|
20
|
+
* Push to github repo
|
21
|
+
|
22
|
+
git push origin --tags
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
docile (1.1.5)
|
5
|
+
multi_json (1.10.1)
|
6
|
+
pg (0.17.1)
|
7
|
+
simplecov (0.9.1)
|
8
|
+
docile (~> 1.1.0)
|
9
|
+
multi_json (~> 1.0)
|
10
|
+
simplecov-html (~> 0.8.0)
|
11
|
+
simplecov-html (0.8.0)
|
12
|
+
statsd-ruby (1.2.1)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
pg
|
19
|
+
simplecov
|
20
|
+
statsd-ruby
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015, MeetMe, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
The MIT License (MIT)
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# pg_metrics PostgreSQL Metrics
|
2
|
+
|
3
|
+
`pg_metrics` is a PostgreSQL metrics collector for use with statsd.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
gem install pg_metrics
|
8
|
+
|
9
|
+
The `pg_metrics_statsd` command is now available.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
To collect PostgreSQL instance metrics on localhost port 5432 and pass them to a
|
14
|
+
statsd instance running on localhost port 8125:
|
15
|
+
|
16
|
+
pg_metrics_statsd --host localhost --port 8125 --connection "host=localhost port=5432"
|
17
|
+
|
18
|
+
To collect PostgreSQL database metrics for the `prod` database, include the
|
19
|
+
`--dbname` parameter:
|
20
|
+
|
21
|
+
pg_metrics_statsd --host localhost --port 8125 --connection "host=localhost port=5432" --dbname=prod
|
22
|
+
|
23
|
+
By default, pg_metrics_statsd collects stats from `pg_locks`,
|
24
|
+
`pg_stat_user_functions` (where available), `pg_stat_user_tables`,
|
25
|
+
`pg_stat_user_tables`, `pg_stat_user_indexes`, `pg_statio_user_indexes`,
|
26
|
+
as well as per-table and per-index sizes. You can omit stats by supplying
|
27
|
+
command line flags:
|
28
|
+
|
29
|
+
- `--no-functions`
|
30
|
+
- `--no-locks`
|
31
|
+
- `--no-table-stats`
|
32
|
+
- `--no-table-statio`
|
33
|
+
- `--no-index-stats`
|
34
|
+
- `--no-index-statio`
|
35
|
+
- `--no-table-sizes`
|
36
|
+
- `--no-index-sizes`
|
37
|
+
|
38
|
+
### pgbouncer metrics
|
39
|
+
|
40
|
+
`pg_metrics` can also collect `pgbouncer` metrics by passing the `--pgbouncer`
|
41
|
+
flag.
|
42
|
+
|
43
|
+
pg_metrics_statsd --host localhost --port 8125 --connection "host=localhost port=6432" --pgbouncer
|
@@ -0,0 +1,347 @@
|
|
1
|
+
require "pg"
|
2
|
+
require "set"
|
3
|
+
|
4
|
+
module PgMetrics
|
5
|
+
module Metrics
|
6
|
+
|
7
|
+
Functions = :functions
|
8
|
+
Locks = :locks
|
9
|
+
TableSizes = :table_size
|
10
|
+
IndexSizes = :index_size
|
11
|
+
TableStatio = :table_statio
|
12
|
+
TableStats = :table_stats
|
13
|
+
IndexStatio = :index_statio
|
14
|
+
IndexStats = :index_stats
|
15
|
+
|
16
|
+
def self.fetch_instance_metrics(app_name, conn_info, regexp = nil)
|
17
|
+
metrics = []
|
18
|
+
conn = make_conn(conn_str(conn_info), app_name)
|
19
|
+
server_version = conn.parameter_status("server_version")
|
20
|
+
instance_metrics(server_version).values.each do |m|
|
21
|
+
metrics += fetch_metrics(conn, m[:prefix], m[:query])
|
22
|
+
end
|
23
|
+
conn.finish
|
24
|
+
filter_metrics(metrics, regexp)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.fetch_database_metrics(app_name, conn_info, dbname, select_names, regexp = nil)
|
28
|
+
metrics = []
|
29
|
+
conn = make_conn(conn_str(conn_info, dbname), app_name)
|
30
|
+
server_version = conn.parameter_status("server_version")
|
31
|
+
select_metrics = database_metrics(server_version).select { |k, _v| select_names.include? k }
|
32
|
+
select_metrics.values.each do |m|
|
33
|
+
metrics += fetch_metrics(conn, ["database", dbname] + m[:prefix], m[:query])
|
34
|
+
end
|
35
|
+
conn.finish
|
36
|
+
filter_metrics(metrics, regexp)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.make_conn(conn_str, app_name)
|
40
|
+
conn = PG::Connection.new(conn_str)
|
41
|
+
server_version = conn.parameter_status("server_version")
|
42
|
+
conn.exec(%(SET application_name = "#{app_name}")) if Gem::Version.new(server_version) >= Gem::Version.new("9.0")
|
43
|
+
conn
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.filter_metrics(metrics, regexp = nil)
|
47
|
+
metrics.reject! { |m| m[1].nil? }
|
48
|
+
metrics.reject! { |m| m[0].any? { |k| k =~ regexp } } if regexp
|
49
|
+
metrics
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.conn_str(conn_info, dbname = "postgres")
|
53
|
+
[conn_info, %(dbname=#{dbname})].join(" ")
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.fetch_metrics(conn, keys, query)
|
57
|
+
metrics = []
|
58
|
+
|
59
|
+
return metrics if query.nil?
|
60
|
+
|
61
|
+
timestamp = Time.now.to_i
|
62
|
+
|
63
|
+
conn.exec(query) do |result|
|
64
|
+
if result.nfields == 1 && result.ntuples == 1
|
65
|
+
# Typically result of SHOW command
|
66
|
+
metrics << format_metric(keys, result.getvalue(0, 0), timestamp)
|
67
|
+
elsif result.nfields >= 2 && result.fields.first == "key"
|
68
|
+
if result.fields.last == "value"
|
69
|
+
# Omit "value" column from metric name
|
70
|
+
nkeys = result.nfields - 1
|
71
|
+
result.each_row do |row|
|
72
|
+
mkeys = row.first(nkeys)
|
73
|
+
value = row.last
|
74
|
+
metrics << format_metric(keys + mkeys, value, timestamp)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
# Use any column named key* as part of the metric name.
|
78
|
+
# Any other columns are named values.
|
79
|
+
nkeys = result.fields.take_while { |f| f =~ /^key/ }.count
|
80
|
+
keycols = result.fields.first(nkeys)
|
81
|
+
nvals = result.nfields - nkeys
|
82
|
+
valcols = result.fields.last(nvals)
|
83
|
+
result.each do |tup|
|
84
|
+
mkeys = keycols.map { |col| tup[col] }
|
85
|
+
valcols.each do |key|
|
86
|
+
value = tup[key]
|
87
|
+
metrics << format_metric(keys + mkeys + [key], value, timestamp)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
else # We've got a single-row result where columns are named values
|
92
|
+
result[0].each do |key, value|
|
93
|
+
metrics << format_metric(keys + [key], value, timestamp)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
metrics
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.format_metric(keys, value, timestamp)
|
102
|
+
segs = keys.reject { |k| k.nil? }.map { |x| x.gsub(/[\s.]/, "_") }
|
103
|
+
value = decode_xlog_location(value)
|
104
|
+
[segs, value, timestamp]
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.decode_xlog_location(val)
|
108
|
+
return val if val.nil?
|
109
|
+
if (m = val.match(%r{([A-Fa-f0-9]+)/([A-Fa-f0-9]+)}))
|
110
|
+
return (m[1].hex << 32) + m[2].hex
|
111
|
+
end
|
112
|
+
val
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.instance_metrics(server_version)
|
116
|
+
{
|
117
|
+
max_connections: {
|
118
|
+
prefix: %w(config instance max_connections),
|
119
|
+
query: %q{SHOW max_connections}
|
120
|
+
},
|
121
|
+
|
122
|
+
superuser_connections: {
|
123
|
+
prefix: %w(config instance superuser_reserved_connections),
|
124
|
+
query: %q{SHOW superuser_reserved_connections}
|
125
|
+
},
|
126
|
+
|
127
|
+
archive_files: {
|
128
|
+
prefix: %w(archive_files),
|
129
|
+
query: %q{SELECT CAST(COALESCE(SUM(CAST(archive_file ~ E'\\.ready$' AS int)), 0) AS INT) AS ready,
|
130
|
+
CAST(COALESCE(SUM(CAST(archive_file ~ E'\\.done$' AS int)), 0) AS INT) AS done
|
131
|
+
FROM pg_catalog.pg_ls_dir('pg_xlog/archive_status') AS archive_files (archive_file)}
|
132
|
+
},
|
133
|
+
|
134
|
+
bgwriter: {
|
135
|
+
prefix: %w(bgwriter),
|
136
|
+
query: %q{SELECT checkpoints_timed, checkpoints_req, buffers_checkpoint,
|
137
|
+
buffers_clean, maxwritten_clean, buffers_backend, buffers_alloc
|
138
|
+
FROM pg_stat_bgwriter}
|
139
|
+
},
|
140
|
+
|
141
|
+
sessions: {
|
142
|
+
prefix: %w(sessions),
|
143
|
+
query: Gem::Version.new(server_version) >= Gem::Version.new('9.2') \
|
144
|
+
? %{SELECT datname AS key, usename AS key2,
|
145
|
+
CASE WHEN waiting THEN 'waiting' ELSE state END AS key3,
|
146
|
+
count(*) AS value
|
147
|
+
FROM pg_stat_activity
|
148
|
+
WHERE pid <> pg_backend_pid() GROUP BY datname, usename, 3}
|
149
|
+
: %{SELECT datname AS key, usename AS key2,
|
150
|
+
CASE WHEN waiting THEN 'waiting'
|
151
|
+
ELSE CASE current_query
|
152
|
+
WHEN NULL THEN 'disabled'
|
153
|
+
WHEN '<IDLE>' THEN 'idle'
|
154
|
+
WHEN '<IDLE> in transaction' THEN 'idle in transaction'
|
155
|
+
ELSE 'active' END END AS key3,
|
156
|
+
count(*) AS value
|
157
|
+
FROM pg_stat_activity
|
158
|
+
WHERE procpid <> pg_backend_pid() GROUP BY datname, usename, 3}
|
159
|
+
},
|
160
|
+
|
161
|
+
database_connection_limits: {
|
162
|
+
prefix: %w(config database),
|
163
|
+
query: %q{SELECT datname AS key,
|
164
|
+
CASE WHEN datconnlimit <> -1 THEN datconnlimit ELSE current_setting('max_connections')::int END AS connection_limit
|
165
|
+
FROM pg_database
|
166
|
+
WHERE datallowconn AND NOT datistemplate}
|
167
|
+
},
|
168
|
+
|
169
|
+
user_connection_limits: {
|
170
|
+
prefix: %w(config user),
|
171
|
+
query: %q{SELECT rolname AS key,
|
172
|
+
CASE WHEN rolconnlimit <> -1 THEN rolconnlimit ELSE current_setting('max_connections')::INT - CASE WHEN rolsuper THEN 0 ELSE current_setting('superuser_reserved_connections')::INT END END AS connection_limit
|
173
|
+
FROM pg_roles
|
174
|
+
WHERE rolcanlogin}
|
175
|
+
},
|
176
|
+
|
177
|
+
database_size: {
|
178
|
+
prefix: %w(database),
|
179
|
+
query: %q{SELECT datname AS key, pg_database_size(oid) AS size FROM pg_database WHERE NOT datistemplate}
|
180
|
+
},
|
181
|
+
|
182
|
+
streaming_state: {
|
183
|
+
prefix: %w(streaming_state),
|
184
|
+
query: Gem::Version.new(server_version) >= Gem::Version.new('9.1') \
|
185
|
+
? %q{SELECT CASE WHEN client_hostname IS NULL THEN 'socket' ELSE host(client_addr) END AS key,
|
186
|
+
CASE state WHEN 'catchup' THEN 1 WHEN 'streaming' THEN 2 ELSE 0 END as value
|
187
|
+
FROM pg_stat_replication}
|
188
|
+
: nil
|
189
|
+
},
|
190
|
+
|
191
|
+
transactions: {
|
192
|
+
prefix: %w(database),
|
193
|
+
query: %q{SELECT dat.datname AS key, 'transactions' AS key2, xact_commit AS commit, xact_rollback AS rollback FROM pg_stat_database JOIN pg_database dat ON dat.oid = datid WHERE datallowconn AND NOT datistemplate}
|
194
|
+
},
|
195
|
+
|
196
|
+
xlog: {
|
197
|
+
prefix: %w(xlog),
|
198
|
+
query: Gem::Version.new(server_version) >= Gem::Version.new('9.0') \
|
199
|
+
? %q{SELECT CASE WHEN pg_is_in_recovery() THEN NULL ELSE pg_current_xlog_location() END AS location,
|
200
|
+
pg_last_xlog_receive_location() AS receive_location,
|
201
|
+
pg_last_xlog_replay_location() AS replay_location}
|
202
|
+
: %q{SELECT pg_current_xlog_location() AS location}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.database_metrics(server_version)
|
208
|
+
{
|
209
|
+
Functions => {
|
210
|
+
prefix: %w(function),
|
211
|
+
query: Gem::Version.new(server_version) >= Gem::Version.new('8.4') \
|
212
|
+
? %q{SELECT schemaname AS key,
|
213
|
+
array_to_string(ARRAY[funcname, '-', pronargs::TEXT,
|
214
|
+
CASE WHEN pronargs = 0 THEN ''
|
215
|
+
ELSE '-' || array_to_string(CASE WHEN pronargs > 16
|
216
|
+
THEN ARRAY(SELECT args[i]
|
217
|
+
FROM generate_series(1, 8) AS _(i))
|
218
|
+
|| '-'::TEXT
|
219
|
+
|| ARRAY(SELECT args[i]
|
220
|
+
FROM generate_series(pronargs - 7, pronargs) AS _ (i))
|
221
|
+
|| funcid::TEXT
|
222
|
+
ELSE args END, '-') END], '') AS key2,
|
223
|
+
calls, total_time, self_time
|
224
|
+
FROM (SELECT funcid, schemaname, funcname::TEXT, pronargs,
|
225
|
+
ARRAY(SELECT typname::TEXT
|
226
|
+
FROM pg_type
|
227
|
+
JOIN (SELECT args.i, proargtypes[args.i] AS typid
|
228
|
+
FROM pg_catalog.generate_series(0, array_upper(proargtypes, 1)) AS args (i))
|
229
|
+
AS args (i, typid) ON typid = pg_type.oid
|
230
|
+
ORDER BY i) AS args,
|
231
|
+
calls, total_time, self_time
|
232
|
+
FROM pg_stat_user_functions
|
233
|
+
JOIN pg_proc ON pg_proc.oid = funcid
|
234
|
+
WHERE schemaname NOT IN ('information_schema', 'pg_catalog')) AS funcs}
|
235
|
+
: nil
|
236
|
+
},
|
237
|
+
|
238
|
+
Locks => {
|
239
|
+
prefix: %w(table),
|
240
|
+
query: %q{SELECT nspname AS key,
|
241
|
+
CASE rel.relkind WHEN 'r' THEN rel.relname ELSE crel.relname END AS key2,
|
242
|
+
CASE rel.relkind WHEN 'r' THEN 'locks' ELSE 'index' END AS key3,
|
243
|
+
CASE rel.relkind WHEN 'r' THEN mode ELSE rel.relname END AS key4,
|
244
|
+
CASE rel.relkind WHEN 'r' THEN NULL ELSE 'locks' END AS key5,
|
245
|
+
CASE rel.relkind WHEN 'r' THEN NULL ELSE mode END AS key6,
|
246
|
+
count(*) AS value
|
247
|
+
FROM pg_locks
|
248
|
+
JOIN pg_database dat ON dat.oid = database
|
249
|
+
JOIN pg_class rel ON rel.oid = relation
|
250
|
+
LEFT JOIN pg_index ON indexrelid = rel.oid
|
251
|
+
LEFT JOIN pg_class crel ON indrelid = crel.oid
|
252
|
+
JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
|
253
|
+
WHERE locktype = 'relation' AND nspname <> 'pg_catalog' AND rel.relkind in ('r', 'i')
|
254
|
+
GROUP BY 1, 2, 3, 4, 5, 6}
|
255
|
+
},
|
256
|
+
|
257
|
+
TableSizes => {
|
258
|
+
prefix: %w(table),
|
259
|
+
query: %q{SELECT n.nspname AS key, r.relname AS key2,
|
260
|
+
pg_relation_size(r.oid) AS size,
|
261
|
+
pg_total_relation_size(r.oid) AS total_size
|
262
|
+
FROM pg_class r
|
263
|
+
JOIN pg_namespace n ON r.relnamespace = n.oid
|
264
|
+
WHERE r.relkind = 'r'
|
265
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')}
|
266
|
+
},
|
267
|
+
|
268
|
+
IndexSizes => {
|
269
|
+
prefix: %w(table),
|
270
|
+
query: %q{SELECT n.nspname AS key, cr.relname AS key2, 'index' AS key3,
|
271
|
+
ci.relname AS key4, pg_relation_size(ci.oid) AS size
|
272
|
+
FROM pg_class ci JOIN pg_index i ON ci.oid = i.indexrelid
|
273
|
+
JOIN pg_class cr ON cr.oid = i.indrelid
|
274
|
+
JOIN pg_namespace n on ci.relnamespace = n.oid
|
275
|
+
WHERE ci.relkind = 'i' AND cr.relkind = 'r'
|
276
|
+
AND n.nspname NOT IN ('pg_catalog', 'information_schema')}
|
277
|
+
},
|
278
|
+
|
279
|
+
TableStatio => {
|
280
|
+
prefix: %w(table),
|
281
|
+
query: %q{SELECT schemaname AS key, relname AS key2, 'statio' AS key3,
|
282
|
+
nullif(heap_blks_read, 0) AS heap_blks_read,
|
283
|
+
nullif(heap_blks_hit, 0) AS heap_blks_hit,
|
284
|
+
nullif(idx_blks_read, 0) AS idx_blks_read,
|
285
|
+
nullif(idx_blks_hit, 0) AS idx_blks_hit,
|
286
|
+
nullif(toast_blks_read, 0) AS toast_blks_read,
|
287
|
+
nullif(toast_blks_hit, 0) AS toast_blks_hit,
|
288
|
+
nullif(tidx_blks_read, 0) AS tidx_blks_read,
|
289
|
+
nullif(tidx_blks_hit, 0) AS tidx_blks_hit
|
290
|
+
FROM pg_statio_user_tables}
|
291
|
+
},
|
292
|
+
|
293
|
+
TableStats => {
|
294
|
+
prefix: %w(table),
|
295
|
+
query: Gem::Version.new(server_version) >= Gem::Version.new('9.1') \
|
296
|
+
? %q{SELECT schemaname AS key, relname AS key2, 'stat' AS key3,
|
297
|
+
nullif(seq_scan, 0) AS seq_scan,
|
298
|
+
nullif(seq_tup_read, 0) AS seq_tup_read,
|
299
|
+
nullif(idx_scan, 0) AS idx_scan,
|
300
|
+
nullif(idx_tup_fetch, 0) AS idx_tup_fetch,
|
301
|
+
nullif(n_tup_ins, 0) AS n_tup_ins,
|
302
|
+
nullif(n_tup_upd, 0) AS n_tup_upd,
|
303
|
+
nullif(n_tup_del, 0) AS n_tup_del,
|
304
|
+
nullif(n_tup_hot_upd, 0) AS n_tup_hot_upd,
|
305
|
+
nullif(n_live_tup, 0) AS n_live_tup,
|
306
|
+
nullif(n_dead_tup, 0) AS n_dead_tup,
|
307
|
+
nullif(vacuum_count, 0) AS vacuum_count,
|
308
|
+
nullif(autovacuum_count, 0) AS autovacuum_count,
|
309
|
+
nullif(analyze_count, 0) AS analyze_count,
|
310
|
+
nullif(autoanalyze_count, 0) AS autoanalyze_count
|
311
|
+
FROM pg_stat_user_tables} \
|
312
|
+
: %q{SELECT schemaname AS key, relname AS key2, 'stat' AS key3,
|
313
|
+
nullif(seq_scan, 0) AS seq_scan,
|
314
|
+
nullif(seq_tup_read, 0) AS seq_tup_read,
|
315
|
+
nullif(idx_scan, 0) AS idx_scan,
|
316
|
+
nullif(idx_tup_fetch, 0) AS idx_tup_fetch,
|
317
|
+
nullif(n_tup_ins, 0) AS n_tup_ins,
|
318
|
+
nullif(n_tup_upd, 0) AS n_tup_upd,
|
319
|
+
nullif(n_tup_del, 0) AS n_tup_del,
|
320
|
+
nullif(n_tup_hot_upd, 0) AS n_tup_hot_upd,
|
321
|
+
nullif(n_live_tup, 0) AS n_live_tup,
|
322
|
+
nullif(n_dead_tup, 0) AS n_dead_tup
|
323
|
+
FROM pg_stat_user_tables},
|
324
|
+
},
|
325
|
+
|
326
|
+
IndexStatio => {
|
327
|
+
prefix: %w(table),
|
328
|
+
query: %q{SELECT schemaname AS key, relname AS key2, 'index' AS key3,
|
329
|
+
indexrelname AS key4, 'statio' AS key5,
|
330
|
+
nullif(idx_blks_read, 0) AS idx_blks_read,
|
331
|
+
nullif(idx_blks_hit, 0) AS idx_blks_hit
|
332
|
+
FROM pg_statio_user_indexes},
|
333
|
+
},
|
334
|
+
|
335
|
+
IndexStats => {
|
336
|
+
prefix: %w(table),
|
337
|
+
query: %q{SELECT schemaname AS key, relname AS key2, 'index' AS key3,
|
338
|
+
indexrelname AS key4, 'stat' AS key5,
|
339
|
+
nullif(idx_scan, 0) AS idx_scan,
|
340
|
+
nullif(idx_tup_read, 0) AS idx_tup_read,
|
341
|
+
nullif(idx_tup_fetch, 0) AS idx_tup_fetch
|
342
|
+
FROM pg_stat_user_indexes}
|
343
|
+
}
|
344
|
+
}
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "pg"
|
2
|
+
|
3
|
+
module PgMetrics
|
4
|
+
module PgbouncerMetrics
|
5
|
+
|
6
|
+
def self.fetch_pgbouncer_metrics(app_name, conn_info)
|
7
|
+
metrics = []
|
8
|
+
conn = make_conn(conn_str(conn_info), app_name)
|
9
|
+
metrics = metrics.concat(fetch_stats_metrics(conn))
|
10
|
+
pool_results = fetch_pools(conn)
|
11
|
+
metrics = metrics.concat(extract_pool_metrics(pool_results))
|
12
|
+
database_results = fetch_databases(conn)
|
13
|
+
metrics = metrics.concat(extract_database_metrics(database_results))
|
14
|
+
metrics = metrics.concat(extract_backend_metrics(database_results, pool_results))
|
15
|
+
conn.finish
|
16
|
+
filter_metrics(metrics)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.make_conn(conn_str, app_name)
|
20
|
+
PG::Connection.new(conn_str)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.conn_str(conn_info, dbname = "pgbouncer", user = "admin")
|
24
|
+
[conn_info, %(dbname=#{dbname}), %(user=#{user})].join(" ")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.filter_metrics(metrics, regexp = nil)
|
28
|
+
metrics.reject! { |m| m[1].nil? }
|
29
|
+
metrics.reject! { |m| m[0].any? { |k| k =~ regexp } } if regexp
|
30
|
+
metrics.inject([]) { |memo, m| memo << [sanitize_key(m[0]), m[1]] }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.fetch_stats_metrics(conn)
|
34
|
+
cols = %w(total_requests total_received total_sent total_query_time avg_req avg_recv avg_sent avg_query)
|
35
|
+
metrics = []
|
36
|
+
conn.exec("SHOW stats") do |results|
|
37
|
+
results.each do |tup|
|
38
|
+
cols.each do |col|
|
39
|
+
metrics << [["stats", tup["database"], col], tup[col]]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
metrics
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.fetch_pools(conn)
|
47
|
+
conn.exec("SHOW pools")
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.extract_pool_metrics(results)
|
51
|
+
cols = %w(cl_active cl_waiting sv_active sv_idle sv_used sv_tested sv_login maxwait)
|
52
|
+
results.inject([]) do |memo, tup|
|
53
|
+
cols.each do |col|
|
54
|
+
memo << [["pools", tup["database"], tup["user"], col], tup[col]]
|
55
|
+
end
|
56
|
+
memo
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.fetch_databases(conn)
|
61
|
+
conn.exec("SHOW databases")
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.extract_database_metrics(results)
|
65
|
+
cols = %w(pool_size reserve_pool)
|
66
|
+
results.inject([]) do |memo, tup|
|
67
|
+
cols.each do |col|
|
68
|
+
memo << [["databases", tup["name"], col], tup[col]]
|
69
|
+
end
|
70
|
+
memo
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.extract_backend_metrics(database_results, pool_results)
|
75
|
+
databases = database_results.inject({}) do |memo, tup|
|
76
|
+
user = tup["force_user"].nil? ? :sameuser : tup["force_user"]
|
77
|
+
host = tup["host"].nil? ? "localhost" : tup["host"]
|
78
|
+
memo[tup["name"]] = {:host => host, :port => tup["port"], :database => tup["database"], :user => user}
|
79
|
+
memo
|
80
|
+
end
|
81
|
+
|
82
|
+
sum_cols = %w(cl_active cl_waiting sv_active sv_idle sv_used sv_tested sv_login)
|
83
|
+
max_cols = %w(max_wait)
|
84
|
+
cols = sum_cols.concat(max_cols)
|
85
|
+
sums = pool_results.inject({}) do |memo, tup|
|
86
|
+
database = databases[tup["database"]]
|
87
|
+
next memo if database.nil?
|
88
|
+
user = database[:user] === :sameuser ? tup["user"] : database[:user]
|
89
|
+
key = [database[:host], database[:port], database[:database], user]
|
90
|
+
vals = memo[key] || {
|
91
|
+
"cl_active" => 0,
|
92
|
+
"cl_waiting" => 0,
|
93
|
+
"sv_active" => 0,
|
94
|
+
"sv_idle" => 0,
|
95
|
+
"sv_used" => 0,
|
96
|
+
"sv_tested" => 0,
|
97
|
+
"sv_login" => 0,
|
98
|
+
"max_wait" => 0
|
99
|
+
}
|
100
|
+
sum_cols.each { |col| vals[col] += tup[col].to_i }
|
101
|
+
max_cols.each { |col| vals[col] = [tup[col].to_i, vals[col]].max }
|
102
|
+
memo[key] = vals
|
103
|
+
memo
|
104
|
+
end
|
105
|
+
sums.inject([]) do |memo, (key, val)|
|
106
|
+
cols.each do |col|
|
107
|
+
k = ["backends"] + key + [col]
|
108
|
+
memo << [k, val[col]]
|
109
|
+
end
|
110
|
+
memo
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.sanitize_key(key)
|
115
|
+
key.inject([]) { |memo, el| memo << el.gsub(/[^-a-zA-Z_0-9]/, "_") }
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'socket'
|
3
|
+
require 'statsd-ruby'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module PgMetrics
|
7
|
+
module Statsd
|
8
|
+
APPNAME = "pg_metrics_statsd"
|
9
|
+
|
10
|
+
def self.main(args)
|
11
|
+
options = self.parse(args)
|
12
|
+
|
13
|
+
if options[:version]
|
14
|
+
STDOUT.puts %(pg_metrics #{PgMetrics::VERSION})
|
15
|
+
return 0
|
16
|
+
end
|
17
|
+
|
18
|
+
if options[:pgbouncer]
|
19
|
+
metrics = PgMetrics::PgbouncerMetrics::fetch_pgbouncer_metrics(APPNAME, options[:conn])
|
20
|
+
else
|
21
|
+
regexp = options[:exclude] ? options[:exclude] : nil
|
22
|
+
|
23
|
+
metrics = if options[:dbname]
|
24
|
+
PgMetrics::Metrics::fetch_database_metrics(APPNAME, options[:conn], options[:dbname],
|
25
|
+
options[:dbstats], regexp)
|
26
|
+
else
|
27
|
+
PgMetrics::Metrics::fetch_instance_metrics(APPNAME, options[:conn], regexp)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
statsd = ::Statsd.new(options[:host], options[:port]).tap do |sd|
|
32
|
+
sd.namespace = options[:scheme]
|
33
|
+
end
|
34
|
+
|
35
|
+
metrics.map! { |m| [m[0].join("."), m[1]] }
|
36
|
+
|
37
|
+
metrics.each { |m| STDOUT.puts m.join(" ") } if options[:verbose]
|
38
|
+
|
39
|
+
metrics.each do |m|
|
40
|
+
statsd.gauge(m[0], m[1])
|
41
|
+
end
|
42
|
+
|
43
|
+
exit 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse(args)
|
47
|
+
options = {
|
48
|
+
host: "localhost",
|
49
|
+
port: 8125,
|
50
|
+
conn: "",
|
51
|
+
scheme: %(#{Socket.gethostname}.postgresql),
|
52
|
+
dbstats: [PgMetrics::Metrics::Functions,
|
53
|
+
PgMetrics::Metrics::Locks,
|
54
|
+
PgMetrics::Metrics::TableSizes,
|
55
|
+
PgMetrics::Metrics::IndexSizes,
|
56
|
+
PgMetrics::Metrics::TableStatio,
|
57
|
+
PgMetrics::Metrics::TableStats,
|
58
|
+
PgMetrics::Metrics::IndexStatio,
|
59
|
+
PgMetrics::Metrics::IndexStats].to_set
|
60
|
+
}
|
61
|
+
|
62
|
+
OptionParser.new do |opts|
|
63
|
+
opts.on("-h", "--host STATSD_HOST", "StatsD host") { |v| options[:host] = v }
|
64
|
+
opts.on("-p", "--port STATSD_PORT", "StatsD port") { |v| options[:port] = v.to_i }
|
65
|
+
opts.on("-c", "--connection CONN", "PostgreSQL connection string") { |v| options[:conn] = v }
|
66
|
+
opts.on("-d", "--dbname DBNAME", "PostgreSQL database name for database metrics") { |v| options[:dbname] = v }
|
67
|
+
opts.on("-e", "--exclude REGEXP", "Exclude objects matching given regexp") { |v| options[:exclude] = ::Regexp.new(v) }
|
68
|
+
opts.on("-s", "--scheme SCHEME", "Metric namespace") { |v| options[:scheme] = v }
|
69
|
+
opts.on("--[no-]functions", "Collect database function stats") { |v| options[:dbstats].delete(PgMetrics::Metrics::Functions) unless v }
|
70
|
+
opts.on("--[no-]locks", "Collect database lock stats") { |v| options[:dbstats].delete(PgMetrics::Metrics::Locks) unless v }
|
71
|
+
opts.on("--[no-]table-sizes", "Collect database table size stats ") { |v| options[:dbstats].delete(PgMetrics::Metrics::TableSizes) unless v }
|
72
|
+
opts.on("--[no-]index-sizes", "Collect database index size stats ") { |v| options[:dbstats].delete(PgMetrics::Metrics::IndexSizes) unless v }
|
73
|
+
opts.on("--[no-]table-statio", "Collect database table statio stats ") { |v| options[:dbstats].delete(PgMetrics::Metrics::TableStatio) unless v }
|
74
|
+
opts.on("--[no-]table-stats", "Collect database table stats ") { |v| options[:dbstats].delete(PgMetrics::Metrics::TableStats) unless v }
|
75
|
+
opts.on("--[no-]index-statio", "Collect database index statio stats ") { |v| options[:dbstats].delete(PgMetrics::Metrics::IndexStatio) unless v }
|
76
|
+
opts.on("--[no-]index-stats", "Collect database index stats ") { |v| options[:dbstats].delete(PgMetrics::Metrics::IndexStats) unless v }
|
77
|
+
opts.on("--pgbouncer", "Collect pgbouncer stats") { |v| options[:pgbouncer] = true }
|
78
|
+
opts.on("--verbose") { |v| options[:verbose] = true }
|
79
|
+
opts.on("--version") { |v| options[:version] = v }
|
80
|
+
end.order!(args)
|
81
|
+
|
82
|
+
options
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/pg_metrics.rb
ADDED
data/pg_metrics.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
lib = File.expand_path("../lib/", __FILE__)
|
2
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
3
|
+
require "rake"
|
4
|
+
require "pg_metrics/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "pg_metrics"
|
8
|
+
spec.version = PgMetrics::VERSION
|
9
|
+
spec.licenses = %w(MIT)
|
10
|
+
spec.date = "2015-03-17"
|
11
|
+
spec.summary = "pg_metrics"
|
12
|
+
spec.description = "PostgreSQL Metrics"
|
13
|
+
spec.authors = ["Michael Glaesemann"]
|
14
|
+
spec.email = ["michael.glaesemann@meetme.com"]
|
15
|
+
spec.files = FileList["{bin,lib,test}/**/*.*",
|
16
|
+
"CHANGELOG.markdown",
|
17
|
+
"DEV.markdown",
|
18
|
+
"Gemfile",
|
19
|
+
"Gemfile.lock",
|
20
|
+
"LICENSE",
|
21
|
+
"README.markdown",
|
22
|
+
"pg_metrics.gemspec",
|
23
|
+
"rakefile"].to_a
|
24
|
+
spec.executables = %w(pg_metrics_statsd)
|
25
|
+
spec.require_path = %(lib)
|
26
|
+
spec.test_files = FileList["test/**/*.*"].to_a
|
27
|
+
spec.extra_rdoc_files = %w(LICENSE README.markdown)
|
28
|
+
spec.homepage = "https://github.com/MeetMe/pg_metrics"
|
29
|
+
[["pg", ["~> 0.10"]],
|
30
|
+
["statsd-ruby", ["~> 1.2", ">= 1.2.1"]]].each do |dep|
|
31
|
+
spec.add_runtime_dependency(*dep)
|
32
|
+
end
|
33
|
+
|
34
|
+
[["test-unit", [["~> 2.1", ">= 2.1.2.0"]]],
|
35
|
+
["simplecov", ["~> 0.7", ">= 0.7.1"]]].each do |dep|
|
36
|
+
spec.add_development_dependency(*dep)
|
37
|
+
end
|
38
|
+
end
|
data/rakefile
ADDED
data/test/helper.rb
ADDED
data/test/test_statsd.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require "helper"
|
2
|
+
require "set"
|
3
|
+
|
4
|
+
module PgMetrics
|
5
|
+
module Test
|
6
|
+
class Statsd < ::Test::Unit::TestCase
|
7
|
+
def test_ok
|
8
|
+
assert(true)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_should_have_sensible_defaults
|
12
|
+
args = %w()
|
13
|
+
config = PgMetrics::Statsd::parse(args)
|
14
|
+
assert_equal("localhost", config[:host])
|
15
|
+
assert_equal(8125, config[:port])
|
16
|
+
assert_equal("", config[:conn])
|
17
|
+
assert_match(/\.postgresql$/, config[:scheme])
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_set_host_and_port
|
21
|
+
args = %w(--host 127.0.0.1 --port 9000)
|
22
|
+
config = PgMetrics::Statsd::parse(args)
|
23
|
+
assert_equal("127.0.0.1", config[:host])
|
24
|
+
assert_equal(9000, config[:port])
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_should_set_regexp_filter
|
28
|
+
args = %w(--exclude xdrop)
|
29
|
+
config = PgMetrics::Statsd::parse(args)
|
30
|
+
assert_equal(config[:exclude], ::Regexp.new(/xdrop/))
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_should_set_connection
|
34
|
+
args = ["--connection", "host=localhost port=5493"]
|
35
|
+
config = PgMetrics::Statsd::parse(args)
|
36
|
+
assert_equal(config[:conn], "host=localhost port=5493")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_set_dbname
|
40
|
+
args = %w(--dbname prod)
|
41
|
+
config = PgMetrics::Statsd::parse(args)
|
42
|
+
assert_equal(config[:dbname], "prod")
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_set_all_metrics
|
46
|
+
args = []
|
47
|
+
config = PgMetrics::Statsd::parse(args)
|
48
|
+
expected = [PgMetrics::Metrics::Functions,
|
49
|
+
PgMetrics::Metrics::Locks,
|
50
|
+
PgMetrics::Metrics::TableSizes,
|
51
|
+
PgMetrics::Metrics::IndexSizes,
|
52
|
+
PgMetrics::Metrics::TableStatio,
|
53
|
+
PgMetrics::Metrics::TableStats,
|
54
|
+
PgMetrics::Metrics::IndexStatio,
|
55
|
+
PgMetrics::Metrics::IndexStats].to_set
|
56
|
+
assert_equal(config[:dbstats], expected)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_set_all_metrics_with_positive_locks
|
60
|
+
args = %w(--locks)
|
61
|
+
config = PgMetrics::Statsd::parse(args)
|
62
|
+
expected = [PgMetrics::Metrics::Functions,
|
63
|
+
PgMetrics::Metrics::Locks,
|
64
|
+
PgMetrics::Metrics::TableSizes,
|
65
|
+
PgMetrics::Metrics::IndexSizes,
|
66
|
+
PgMetrics::Metrics::TableStatio,
|
67
|
+
PgMetrics::Metrics::TableStats,
|
68
|
+
PgMetrics::Metrics::IndexStatio,
|
69
|
+
PgMetrics::Metrics::IndexStats].to_set
|
70
|
+
assert_equal(config[:dbstats], expected)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_should_not_collect_locks
|
74
|
+
args = %w(--no-locks)
|
75
|
+
config = PgMetrics::Statsd::parse(args)
|
76
|
+
expected = [PgMetrics::Metrics::Functions,
|
77
|
+
PgMetrics::Metrics::TableSizes,
|
78
|
+
PgMetrics::Metrics::IndexSizes,
|
79
|
+
PgMetrics::Metrics::TableStatio,
|
80
|
+
PgMetrics::Metrics::TableStats,
|
81
|
+
PgMetrics::Metrics::IndexStatio,
|
82
|
+
PgMetrics::Metrics::IndexStats].to_set
|
83
|
+
assert_equal(config[:dbstats], expected)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_should_remove_all_but_locks
|
87
|
+
args = %w(--no-functions --no-table-sizes --no-index-sizes --no-table-statio --no-table-stats --no-index-stats --no-index-statio)
|
88
|
+
config = PgMetrics::Statsd::parse(args)
|
89
|
+
expected = [PgMetrics::Metrics::Locks].to_set
|
90
|
+
assert_equal(config[:dbstats], expected)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pg_metrics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Glaesemann
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: statsd-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.2.1
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.2'
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.2.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: test-unit
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.1'
|
54
|
+
- - '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 2.1.2.0
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ~>
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '2.1'
|
64
|
+
- - '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 2.1.2.0
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: simplecov
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0.7'
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.7.1
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.7'
|
84
|
+
- - '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 0.7.1
|
87
|
+
description: PostgreSQL Metrics
|
88
|
+
email:
|
89
|
+
- michael.glaesemann@meetme.com
|
90
|
+
executables:
|
91
|
+
- pg_metrics_statsd
|
92
|
+
extensions: []
|
93
|
+
extra_rdoc_files:
|
94
|
+
- LICENSE
|
95
|
+
- README.markdown
|
96
|
+
files:
|
97
|
+
- lib/pg_metrics.rb
|
98
|
+
- lib/pg_metrics/metrics.rb
|
99
|
+
- lib/pg_metrics/pgbouncer_metrics.rb
|
100
|
+
- lib/pg_metrics/statsd.rb
|
101
|
+
- lib/pg_metrics/version.rb
|
102
|
+
- test/helper.rb
|
103
|
+
- test/test_statsd.rb
|
104
|
+
- CHANGELOG.markdown
|
105
|
+
- DEV.markdown
|
106
|
+
- Gemfile
|
107
|
+
- Gemfile.lock
|
108
|
+
- LICENSE
|
109
|
+
- README.markdown
|
110
|
+
- pg_metrics.gemspec
|
111
|
+
- rakefile
|
112
|
+
- bin/pg_metrics_statsd
|
113
|
+
homepage: https://github.com/MeetMe/pg_metrics
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.0.3
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: pg_metrics
|
137
|
+
test_files:
|
138
|
+
- test/helper.rb
|
139
|
+
- test/test_statsd.rb
|