kanshi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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_.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'kanshi'
6
+
7
+ Kanshi.run(
8
+ :databases => ENV.select { |k, v| v =~ %r{^postgres://} }.invert.invert,
9
+ :delay => (ENV['KANSHI_SAMPLE_DELAY'] || 300).to_i
10
+ )
@@ -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: []