kanshi 0.0.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.
- data/README.md +42 -0
- data/bin/kanshi +10 -0
- data/lib/kanshi.rb +38 -0
- data/lib/kanshi/collector.rb +32 -0
- data/lib/kanshi/queries.rb +34 -0
- data/lib/kanshi/scrolls_reporter.rb +49 -0
- metadata +100 -0
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# 監視
|
2
|
+
|
3
|
+
## Purpose
|
4
|
+
|
5
|
+
Kanshi watches your Postgres database and reports metrics to the log
|
6
|
+
stream. You can then drain this log to an application - or fork and
|
7
|
+
modify kanshi itself - to send these metrics to a service, like Librato
|
8
|
+
or Splunk.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Kanshi will work as a gem or as a standalone app.
|
13
|
+
|
14
|
+
### Add to existing app on Heroku
|
15
|
+
|
16
|
+
1. Add Kanshi to your Gemfile:
|
17
|
+
|
18
|
+
gem 'kanshi', :require => false
|
19
|
+
|
20
|
+
2. Add Kanshi to your Procfile:
|
21
|
+
|
22
|
+
kanshi: bundle exec kanshi
|
23
|
+
|
24
|
+
3. Run `bundle install`, commit, and deploy.
|
25
|
+
4. `heroku scale kanshi=1`
|
26
|
+
|
27
|
+
### Create as separate Heroku app
|
28
|
+
|
29
|
+
1. Push the code as-is to a new Heroku app.
|
30
|
+
2. Set environment variables with database URLs.
|
31
|
+
3. `heroku scale kanshi=1`
|
32
|
+
|
33
|
+
### Custom install
|
34
|
+
|
35
|
+
You can create your own version of `bin/kanshi` to run Kanshi in any way
|
36
|
+
of your choosing, e.g. monitor specific databases or provide your own
|
37
|
+
reporter for output. An easy way to do this would be to create a Rake
|
38
|
+
task to run Kanshi.
|
39
|
+
|
40
|
+
## Name
|
41
|
+
|
42
|
+
Kanshi (監視) is Japanese for _surveillance_.
|
data/bin/kanshi
ADDED
data/lib/kanshi.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Kanshi = Class.new
|
2
|
+
|
3
|
+
require 'kanshi/collector'
|
4
|
+
require 'kanshi/scrolls_reporter'
|
5
|
+
|
6
|
+
class Kanshi
|
7
|
+
|
8
|
+
def self.run(*args)
|
9
|
+
self.new(*args).run
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
@options = {
|
14
|
+
:databases => [],
|
15
|
+
:delay => 300,
|
16
|
+
:reporter => ScrollsReporter
|
17
|
+
}
|
18
|
+
@options.merge!(options)
|
19
|
+
@reporter = @options[:reporter].new
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
loop do
|
24
|
+
report
|
25
|
+
sleep(@options[:delay])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def report
|
30
|
+
@options[:databases].each do |name, database_url|
|
31
|
+
@reporter.report(
|
32
|
+
name,
|
33
|
+
database_url,
|
34
|
+
Collector.collect(database_url))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'kanshi/queries'
|
3
|
+
|
4
|
+
class Kanshi::Collector
|
5
|
+
|
6
|
+
def self.collect(*args)
|
7
|
+
new(*args).collect
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(database_url)
|
11
|
+
@url = database_url
|
12
|
+
@db_name = URI.parse(@url).path[1..-1]
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_db(&block)
|
16
|
+
db = Sequel.connect(@url)
|
17
|
+
yield db
|
18
|
+
ensure
|
19
|
+
db.disconnect if db
|
20
|
+
end
|
21
|
+
|
22
|
+
def collect
|
23
|
+
data = {}
|
24
|
+
with_db do |db|
|
25
|
+
::Kanshi::Queries.each do |query|
|
26
|
+
data.merge! db[query, @db_name].first
|
27
|
+
end
|
28
|
+
end
|
29
|
+
data
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Kanshi::Queries = <<EOF.split(/\s*;\s*/)
|
2
|
+
SELECT
|
3
|
+
pg_database_size(d.datname) as size,
|
4
|
+
numbackends,
|
5
|
+
xact_commit,
|
6
|
+
xact_rollback,
|
7
|
+
blks_read,
|
8
|
+
blks_hit,
|
9
|
+
tup_fetched,
|
10
|
+
tup_returned,
|
11
|
+
tup_inserted,
|
12
|
+
tup_updated,
|
13
|
+
tup_deleted
|
14
|
+
FROM
|
15
|
+
pg_stat_database d
|
16
|
+
WHERE
|
17
|
+
d.datname = ?;
|
18
|
+
|
19
|
+
SELECT
|
20
|
+
SUM(seq_scan)::bigint AS seq_scan,
|
21
|
+
SUM(seq_tup_read)::bigint AS seq_tup_read,
|
22
|
+
SUM(idx_scan)::bigint AS idx_scan,
|
23
|
+
SUM(idx_tup_fetch)::bigint AS idx_tup_fetch
|
24
|
+
FROM
|
25
|
+
pg_stat_user_tables;
|
26
|
+
|
27
|
+
SELECT
|
28
|
+
SUM(heap_blks_read)::bigint AS heap_blks_read,
|
29
|
+
SUM(heap_blks_hit)::bigint AS heap_blks_hit,
|
30
|
+
SUM(idx_blks_read)::bigint AS idx_blks_read,
|
31
|
+
SUM(idx_blks_hit)::bigint AS idx_blks_hit
|
32
|
+
FROM
|
33
|
+
pg_statio_user_tables;
|
34
|
+
EOF
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'scrolls'
|
2
|
+
|
3
|
+
class Kanshi::ScrollsReporter
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@last_value = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
ABSOLUTE = [:size, :numbackends]
|
10
|
+
|
11
|
+
def report(name, url, data)
|
12
|
+
data = calculate_hit_rate(record_and_diff(name, data))
|
13
|
+
if data
|
14
|
+
Scrolls.context(:app => "kanshi.#{name}", :measure => true) do
|
15
|
+
data.each do |k, v|
|
16
|
+
Scrolls.log(:at => k, :last => v)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def record_and_diff(name, data)
|
25
|
+
diff = nil
|
26
|
+
if @last_value[name]
|
27
|
+
diff = Hash.new
|
28
|
+
data.keys.each do |key|
|
29
|
+
diff[key] = data[key] - @last_value[name][key]
|
30
|
+
end
|
31
|
+
ABSOLUTE.each do |key|
|
32
|
+
diff["absolute_#{key}"] = data[key]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@last_value[name] = data
|
36
|
+
diff
|
37
|
+
end
|
38
|
+
|
39
|
+
def calculate_hit_rate(data)
|
40
|
+
return nil unless data
|
41
|
+
hit = data[:blks_hit] || 0
|
42
|
+
read = data[:blks_read] || 0
|
43
|
+
if hit + read > 0
|
44
|
+
data[:cache_hit_ratio] = 100.0 * hit / (hit + read)
|
45
|
+
end
|
46
|
+
data
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kanshi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jonathan Dance
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: scrolls
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0.2'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0.2'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: pg
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Prints Postgres database metrics to your log stream
|
63
|
+
email: jd@heroku.com
|
64
|
+
executables:
|
65
|
+
- kanshi
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- README.md
|
70
|
+
- bin/kanshi
|
71
|
+
- lib/kanshi.rb
|
72
|
+
- lib/kanshi/collector.rb
|
73
|
+
- lib/kanshi/queries.rb
|
74
|
+
- lib/kanshi/scrolls_reporter.rb
|
75
|
+
homepage: https://github.com/heroku/kanshi
|
76
|
+
licenses:
|
77
|
+
- MIT
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.23
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Monitors a Postgres database
|
100
|
+
test_files: []
|