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.
@@ -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: []