mysampler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4adc593e649aa58ffb331e5ec1ee89432e25fd9a
4
+ data.tar.gz: 7fe23cf3f0aa2206aebae8a98066b7c287dbf1b2
5
+ SHA512:
6
+ metadata.gz: 34212bb64ca530d7e3dfc12dc6ce47e25b93466ca380d82ce617a13aff2aec6462ba037b498e1547ca033fca7e3741ef283c3d2b009a646f0f1b9e964b8afb26
7
+ data.tar.gz: de9470ccf9822970cbebf9e5892c1e3b02eb763d17acb98786cf953dfc3b06cf5717df9631608bc830ff334c76e0142dcc034d62099fbc329a0dc52ecb3f9496
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.swp
2
+ *.pid
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 2.1.1@mysampler --create --install
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ MySampler
2
+ =========
3
+
4
+ What is it?
5
+ -----------
6
+ MySampler is a tool written in ruby to poll SHOW GLOBAL STATUS in MySQL and output the values to either a CSV or graphite/carbon.
7
+ The interval at which the polling occurs can be specified and the output can be either the absolute or relative values, so you can see change over time.
8
+ If logging to CSV, the a date stamp is appended to the CSV file and it is rotated hourly (to be configurable later).
9
+
10
+ Dependencies
11
+ ------------
12
+
13
+ MySampler requires the following gems:
14
+
15
+ graphite
16
+ sequel
17
+
18
+ Installation
19
+ ------------
20
+ To install, simply install the dependencies above, and clone the repository and run mysample.rb
21
+
22
+ gem install sequel
23
+ gem install graphite
24
+ git clone https://github.com/9minutesnooze/mysampler.git
25
+ cd mysampler
26
+ ./mysample.rb -o csv -u me -p secret -H localhost -f /tmp/mysample.csv -i 10 -r -d -k start
27
+
28
+ Options
29
+ -------
30
+ Usage ./mysample.rb [OPTIONS]
31
+ -u, --user USER MySQL User
32
+ -p, --pass PASSWORD MySQL Password
33
+ -P, --port PORT MySQL port (default 3306)
34
+ --pidfile PIDFILE PID File (default: `pwd`/mysample.pid)
35
+ -H, --host HOST MySQL hostname (default: localhost)
36
+ -f, --file FILENAME output filename (will be appended with rotation timestamp)
37
+ -o, --output (csv|graphite) Output format (default: csv)
38
+ -i, --sleep SECONDS Interval between runs (default: 10)
39
+ -r, --relative Show the difference between the current and previous values (default: false)
40
+ -d, --daemonize daemonize process (default: false)
41
+ -k (start|stop|status) command to pass daemon
42
+ --command
43
+ -g, --graphite HOST:PORT Graphite server:port
44
+ -h, --help this message
45
+
46
+ If daemonized with -d, currently STDERR/STDOUT does not go anywhere, so if you are having problems, try running it without the -d flag initially.
47
+
48
+ Caveats
49
+ -------
50
+ This project, while it runs in production, is rapidly changing so the command line parameters and output are not set in stone.
51
+ I will attempt to write release notes if something changes drastically.
52
+ Currently a lot of the object structure is being revamped and I am adding more features such as SHOW MUTEX STATUS, and SHOW ENGINE INNODB STATUS.
53
+ Those features are initially available in the class_refactor branch.
data/bin/mysampler ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/mysampler'
4
+ require 'optparse'
5
+ require 'sequel'
6
+ require 'logger'
7
+
8
+ $options = { :dbuser => nil,
9
+ :dbpass => nil,
10
+ :dbhost => "localhost",
11
+ :dbport => 3306,
12
+ :dbsocket => nil,
13
+ :daemonize => false,
14
+ :pidfile => Dir.pwd + "/mysample.pid",
15
+ :interval => 10, #seconds
16
+ :command => MySampler::ProcessCtl::STARTCMD ,
17
+ :output => MySampler::Client::CSVOUT,
18
+ :relative => false,
19
+ :graphitehost => nil, }
20
+
21
+ opts = OptionParser.new
22
+ opts.banner = "Usage #{$0} [OPTIONS]"
23
+ opts.on("-u", "--user USER", String, "MySQL User" ) { |v| $options[:dbuser] = v }
24
+ opts.on("-p", "--pass PASSWORD", String, "MySQL Password" ) { |v| $options[:dbpass] = v }
25
+ opts.on("-P", "--port PORT", Integer, "MySQL port (default #{$options[:dbport]})" ) { |v| $options[:dbport] = v }
26
+ opts.on("--pidfile PIDFILE", String, "PID File (default: #{$options[:pidfile]})" ) { |v| $options[:pidfile] = v }
27
+ opts.on("-H", "--host HOST", String, "MySQL hostname (default: #{$options[:dbhost]})" ) { |v| $options[:dbhost] = v }
28
+ opts.on("-f", "--file FILENAME", String, "output filename (will be appended with rotation timestamp)" ) { |v| $options[:outputfn] = v }
29
+ opts.on("-o", "--output (csv|graphite)", String, "Output format (default: csv)" ) do |v|
30
+ $options[:output] = case v
31
+ when "yaml"
32
+ MySampler::Client::YAMLOUT
33
+ when "csv"
34
+ MySampler::Client::CSVOUT
35
+ when "graphite"
36
+ require 'graphite/logger'
37
+ MySampler::Client::GRAPHITEOUT
38
+ else
39
+ puts opts
40
+ exit 1
41
+ end
42
+ end
43
+ opts.on("-i", "--sleep SECONDS", Integer, "Interval between runs (default: #{$options[:interval]})" ) { |v| $options[:interval] = v }
44
+ opts.on("-r", "--relative","Show the difference between the current and previous values (default: #{$options[:relative]})" ) { |v| $options[:relative] = v }
45
+ opts.on("-d", "--daemonize", "daemonize process (default: #{$options[:daemonize]})" ) { |v| $options[:daemonize] = true }
46
+ opts.on("-k", "--command (start|stop|status)", String, "command to pass daemon") do |v|
47
+ $options[:command] = case v
48
+ when "stop"
49
+ MySampler::ProcessCtl::STOPCMD
50
+ when "status"
51
+ MySampler::ProcessCtl::STATUSCMD
52
+ when "start"
53
+ MySampler::ProcessCtl::STARTCMD
54
+ else
55
+ puts opts
56
+ exit 1
57
+ end
58
+ end
59
+ opts.on("-g", "--graphite HOST:PORT", String, "Graphite server:port") { |v| $options[:graphitehost] = v }
60
+ opts.on("-h", "--help", "this message") { puts opts; exit 1}
61
+ opts.parse!
62
+
63
+ pc = MySampler::ProcessCtl.new
64
+ pc.daemonize = $options[:daemonize]
65
+ pc.pidfile = $options[:pidfile]
66
+
67
+
68
+ ms = MySampler::Client.new
69
+ ms.user = $options[:dbuser]
70
+ ms.pass = $options[:dbpass]
71
+ ms.host = $options[:dbhost]
72
+ ms.port = $options[:dbport]
73
+ ms.socket = $options[:dbsocket]
74
+ ms.interval = $options[:interval]
75
+ ms.output = $options[:output]
76
+ ms.relative = $options[:relative]
77
+ ms.outputfn = $options[:outputfn] if $options[:outputfn]
78
+ ms.rotateinterval = MySampler::FileRotating::HOUR
79
+ ms.graphitehost = $options[:graphitehost] if ms.output == MySampler::Client::GRAPHITEOUT
80
+
81
+ case $options[:command]
82
+ when MySampler::ProcessCtl::STOPCMD
83
+ pc.stop { puts "I'm done" }
84
+ when MySampler::ProcessCtl::STATUSCMD
85
+ exit pc.status
86
+ else
87
+ exit pc.start { ms.run }
88
+ end
@@ -0,0 +1,170 @@
1
+ require 'sequel'
2
+
3
+ module MySampler
4
+ class Client
5
+ CSVOUT, YAMLOUT,GRAPHITEOUT = 0,1,2
6
+ attr_accessor :user, :pass, :port, :socket, :host, :interval, :output, :relative, :outputfn, :rotateinterval, :graphitehost
7
+
8
+ def initialize
9
+ @user = nil
10
+ @pass = nil
11
+ @port = 3306
12
+ @socket = nil
13
+ @host = "localhost"
14
+ @query = 'SHOW /*!50002 GLOBAL */ STATUS'
15
+ @interval = 10
16
+ @relative = false
17
+ @output = CSVOUT
18
+ @prev_rows = {}
19
+ @outputfn = nil
20
+ @rotateinterval = FileRotating::HOUR
21
+ @rf = nil
22
+ @graphitehost = nil
23
+ @graphite = nil
24
+ @mysql_hostname = nil
25
+ end
26
+
27
+ def run
28
+ @sequel = db_connect
29
+ if @output == GRAPHITEOUT
30
+ get_mysql_hostname
31
+ conn_to_graphite if @output == GRAPHITEOUT
32
+ else
33
+ headers = get_header_rows
34
+ open_rotating_file(:header => headers.join(","), :interval => @rotateinterval)
35
+ end
36
+
37
+ first_run = true
38
+ loop do
39
+ begin
40
+ rows = @sequel[@query].to_hash(:Variable_name,:Value)
41
+ rows = values_to_numeric(rows)
42
+ rows = calc_relative(rows) if @relative
43
+ rows = scale_values(rows)
44
+ output_query(rows) unless first_run && @relative
45
+ first_run = false
46
+ rescue Exception => e
47
+ STDERR.puts "An error occurred #{e}"
48
+ end
49
+
50
+ sleep @interval
51
+ end
52
+ @rf.close if @rf && @outputfn
53
+ end
54
+
55
+ # get the real hostname of the MySQL Server that we are connected to
56
+ def get_mysql_hostname
57
+ @mysql_hostname = @sequel["SELECT @@hostname;"].first[:@@hostname]
58
+ end
59
+
60
+ def get_header_rows
61
+ @sequel[@query].to_hash(:Variable_name,:Value).keys
62
+ end
63
+
64
+ def output_header(rows)
65
+ @rf.puts(header) if @rf && @outputfn
66
+ end
67
+
68
+ def conn_to_graphite
69
+ @graphite = Graphite::Logger.new(@graphitehost)
70
+ # @graphite.logger = Logger.new('graphite.out')
71
+ end
72
+
73
+ def open_rotating_file (params)
74
+ @rf = @outputfn ? FileRotating.new(params, @outputfn, "w") : STDOUT
75
+ end
76
+
77
+ def is_counter? (key)
78
+ # list lovingly stolen from pt-mysql-summary
79
+ !%w[ Compression Delayed_insert_threads Innodb_buffer_pool_pages_data
80
+ Innodb_buffer_pool_pages_dirty Innodb_buffer_pool_pages_free
81
+ Innodb_buffer_pool_pages_latched Innodb_buffer_pool_pages_misc
82
+ Innodb_buffer_pool_pages_total Innodb_data_pending_fsyncs
83
+ Innodb_data_pending_reads Innodb_data_pending_writes
84
+ Innodb_os_log_pending_fsyncs Innodb_os_log_pending_writes
85
+ Innodb_page_size Innodb_row_lock_current_waits Innodb_row_lock_time_avg
86
+ Innodb_row_lock_time_max Key_blocks_not_flushed Key_blocks_unused
87
+ Key_blocks_used Last_query_cost Max_used_connections Ndb_cluster_node_id
88
+ Ndb_config_from_host Ndb_config_from_port Ndb_number_of_data_nodes
89
+ Not_flushed_delayed_rows Open_files Open_streams Open_tables
90
+ Prepared_stmt_count Qcache_free_blocks Qcache_free_memory
91
+ Qcache_queries_in_cache Qcache_total_blocks Rpl_status
92
+ Slave_open_temp_tables Slave_running Ssl_cipher Ssl_cipher_list
93
+ Ssl_ctx_verify_depth Ssl_ctx_verify_mode Ssl_default_timeout
94
+ Ssl_session_cache_mode Ssl_session_cache_size Ssl_verify_depth
95
+ Ssl_verify_mode Ssl_version Tc_log_max_pages_used Tc_log_page_size
96
+ Threads_cached Threads_connected Threads_running
97
+ Uptime_since_flush_status ].include? key
98
+ end
99
+
100
+ def calc_relative(rows)
101
+ result = {}
102
+ rows.each do |k,v|
103
+ if @prev_rows[k] && numeric?(v) && is_counter?(k)
104
+ result[k] = v - @prev_rows[k]
105
+ else
106
+ result[k] = v
107
+ end
108
+ end
109
+ @prev_rows = rows
110
+ return result
111
+ end
112
+
113
+ def prefix_keys ( h, prefix )
114
+ Hash[h.map { |k,v| [ "#{prefix}#{k}", v] }]
115
+ end
116
+
117
+ def numeric? (value)
118
+ true if Float(value) rescue false
119
+ end
120
+
121
+ def to_numeric (value)
122
+ numeric?(value) ? value.to_i : value
123
+ end
124
+
125
+ # scale the value to be per @interval if recording relative values
126
+ # since it doesn't make much sense to output values that are "per 5 seconds"
127
+ def scale_value (value)
128
+ (@relative && numeric?(value)) ? (value/@interval) : value
129
+ end
130
+
131
+ def scale_values ( rows )
132
+ Hash[rows.map do |k,v|
133
+ is_counter?(k) ? [k, scale_value(v)] : [k, v]
134
+ end]
135
+ end
136
+
137
+ def values_to_numeric ( h )
138
+ Hash[h.map { |k,v| [ k, to_numeric(v)] }]
139
+ end
140
+
141
+ def output_query (rows )
142
+ case @output
143
+ when YAMLOUT then
144
+ # result = YAML::dump({time => Time.now, rows})
145
+ when GRAPHITEOUT then
146
+ graphite_rows = prefix_keys(rows, "mysql.#{@mysql_hostname.split('.').reverse.join('.')}.")
147
+ @graphite.log(Time.now.to_i, graphite_rows) if @graphite
148
+ else # CSVOUT
149
+ @rf.puts(hash_to_csv(rows)) if @rf && @outputfn
150
+ end
151
+ true
152
+ end
153
+
154
+ def hash_to_csv ( rows, header = false )
155
+ str = header ? "Time" : "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
156
+ rows.sort.each { |v| str += header ? ",#{v[0]}" : ",#{v[1]}" }
157
+ return str
158
+ end
159
+
160
+ def db_connect
161
+ params = { :host => @host,
162
+ :user => @user,
163
+ :port => @port,
164
+ :password => @pass }
165
+ params[:socket] = @socket if @socket
166
+ @sequel = Sequel.mysql(params)
167
+ end
168
+ end
169
+
170
+ end
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'yaml'
4
+ module MySampler
5
+
6
+ class FileRotating
7
+ YEAR, MONTH, DAY, HOUR, MINUTE, SECOND = 0,1,2,3,4,5
8
+
9
+ attr_accessor :header
10
+
11
+ def initialize (params, *args)
12
+ @interval = params[:interval] || DAY
13
+ @header = params[:header] || nil # a header to put at the top of every file
14
+ @args = args
15
+ @root_fn = args[0]
16
+ @stamp = get_date_stamp
17
+
18
+ open_local do |f|
19
+ f.puts @header if @header
20
+ if block_given?
21
+ return yield f
22
+ else
23
+ return f
24
+ end
25
+ end
26
+ end
27
+
28
+ def close
29
+ if @f
30
+ @f.flock(File::LOCK_UN)
31
+ @f.close
32
+ end
33
+ end
34
+
35
+ private
36
+ def open_local
37
+ fn = sprintf("%s.%s",@root_fn,@stamp)
38
+ args = @args
39
+ args[0] = fn
40
+ # puts fn
41
+
42
+ begin
43
+ @f = File.open(*args)
44
+ @f.flock(File::LOCK_EX) if @f
45
+ if block_given?
46
+ return yield self
47
+ else
48
+ return self
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ def get_date_stamp
55
+ format = case @interval
56
+ when YEAR then '%Y'
57
+ when MONTH then '%Y%m'
58
+ when DAY then '%Y%m%d'
59
+ when HOUR then '%Y%m%d%H'
60
+ when MINUTE then '%Y%m%d%H%M'
61
+ when SECOND then '%Y%m%d%H%M%S'
62
+ else raise "Invalid interval"
63
+ end
64
+ return Time.now.strftime(format)
65
+ end
66
+
67
+ def method_missing(method, *args, &block)
68
+ # check to see if we need to reopen
69
+ stamp = get_date_stamp
70
+
71
+ if @stamp != stamp
72
+ @stamp = stamp
73
+ close
74
+ open_local
75
+ @f.puts @header if @header
76
+ end
77
+ return @f.send(method, *args, &block)
78
+ end
79
+
80
+ end
81
+
82
+ #FileRotating.new({:interval => FileRotating::SECOND, :header => "Header!!!"}, "/tmp/foo.txt", "w") do |f|
83
+ # 5.times do
84
+ # f.puts Time.now
85
+ # f.puts "Foo!"
86
+ # sleep 1
87
+ # end
88
+ #end
89
+
90
+ end
@@ -0,0 +1,92 @@
1
+ module MySampler
2
+ class ProcessCtl
3
+ STARTCMD, STOPCMD, STATUSCMD = 0,1,2
4
+
5
+ attr_accessor :pidfile, :daemonize
6
+
7
+
8
+ def initialize
9
+ @pidfile = ""
10
+ @daemonize = false
11
+ @pid = nil
12
+ end
13
+
14
+
15
+ def start
16
+ trap(:INT) { stop }
17
+ trap(:SIGTERM) { cleanup }
18
+
19
+ size = get_running_pids.size
20
+ if size > 0
21
+ puts "Daemon is already running"
22
+ return 1
23
+ end
24
+
25
+ # Daemonize.daemonize if @daemonize
26
+ if @daemonize
27
+ #http://stackoverflow.com/questions/1740308/create-a-daemon-with-double-fork-in-ruby
28
+ raise 'First fork failed' if (pid = fork) == -1
29
+ exit unless pid.nil?
30
+
31
+ Process.setsid
32
+ raise 'Second fork failed' if (pid = fork) == -1
33
+ exit unless pid.nil?
34
+
35
+ Dir.chdir '/'
36
+ File.umask 0000
37
+ STDIN.reopen '/dev/null'
38
+ STDOUT.reopen '/dev/null', 'a'
39
+ STDERR.reopen STDOUT
40
+ end
41
+ write_pid unless pidfile == ""
42
+ yield
43
+ return 0
44
+ end
45
+
46
+ def stop
47
+ # call user code if defined
48
+ begin
49
+ yield
50
+ rescue
51
+ end
52
+ get_running_pids.each do |pid|
53
+ puts "Killing pid #{pid}"
54
+ Process.kill :SIGTERM, pid
55
+ # can't do anything below here. Process is dead
56
+ end
57
+ return 0
58
+ end
59
+
60
+ # returns the exit status (1 if not running, 0 if running)
61
+ def status
62
+ size = get_running_pids.size
63
+ puts "#{File.basename $0} is #{"not " if size < 1}running."
64
+ return (size > 0) ? 0 : 1
65
+ end
66
+
67
+ protected
68
+ def cleanup
69
+ File.delete(@pidfile) if File.file?(@pidfile)
70
+ exit 0
71
+ end
72
+
73
+ def write_pid
74
+ @pid = Process.pid
75
+ File.open(@pidfile, "w") do |f|
76
+ # f.write($$)
77
+ f.write(Process.pid)
78
+ end
79
+ end
80
+
81
+ def get_running_pids
82
+ return [@pid] if @pid
83
+ result = []
84
+ if File.file? @pidfile
85
+ pid = File.read @pidfile
86
+ # big long line I stole to kill a pid
87
+ result = `ps -p #{pid} -o pid | sed 1d`.to_a.map!{|x| x.to_i}
88
+ end
89
+ return result
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ module MySampler
2
+ VERSION = "0.0.1"
3
+ end
data/lib/mysampler.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative 'mysampler/client'
2
+ require_relative 'mysampler/file'
3
+ require_relative 'mysampler/processctl'
4
+ require_relative 'mysampler/version'
data/mysampler.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mysampler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mysampler"
8
+ spec.version = MySampler::VERSION
9
+ spec.authors = ["Aaron Brown"]
10
+ spec.email = ["aaron@9minutesnooze.com"]
11
+ spec.summary = %q{A utility that logs MySQL statistics to CSV or graphite.}
12
+ spec.description = %q{MySampler is a tool written in ruby to poll SHOW GLOBAL STATUS in MySQL and output the values to either a CSV or graphite/carbon. The interval at which the polling occurs can be specified and the output can be either the absolute or relative values, so you can see change over time. If logging to CSV, the a date stamp is appended to the CSV file and it is rotated hourly (to be configurable later).}
13
+ spec.homepage = "https://github.com/9minutesnooze/mysampler"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ %w{mysql sequel graphite}.each { |gem| spec.add_dependency gem }
24
+ end
data/spec/tests.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'timecop'
2
+ require 'mysqlsampler'
3
+
4
+
5
+ describe "database connectivity" do
6
+ before(:all) do
7
+ @ms = MySQLSampler.new
8
+ @ms.user = "aaron"
9
+ @ms.pass = "n0m0r3181"
10
+ @ms.host = "locutus.borg.lan"
11
+ @ms.port = 3306
12
+ @ms.db_connect
13
+ end
14
+
15
+ it "should return 'locutus.borg.lan'" do
16
+ @ms.get_mysql_hostname.should == @ms.host
17
+ end
18
+
19
+ it "should return an array that includes 'Uptime'" do
20
+ @ms.get_header_rows.should include('Uptime')
21
+ end
22
+
23
+ end
24
+
25
+ describe "Row operations" do
26
+ before(:all) do
27
+ Timecop.freeze(Time.local(2011,10,17,0,0,0))
28
+ @ms = MySQLSampler.new
29
+ end
30
+
31
+ after(:all) do
32
+ Timecop.return
33
+ end
34
+
35
+ # test relative operation
36
+ it "should return a key with 2 as the value" do
37
+ @ms.calc_relative({"Foo" => 4})
38
+ h = @ms.calc_relative({"Foo" => 6})
39
+ h["Foo"].should == 2
40
+ end
41
+
42
+ it "should prefix key with 'foo.'" do
43
+ h = @ms.prefix_keys({"bar" => 1}, "foo.")
44
+ h.keys.first.should == "foo.bar"
45
+ end
46
+
47
+ it "should convert a string to a numeric if it is a number" do
48
+ @ms.to_numeric("5").should == 5
49
+ end
50
+
51
+ it "should not convert a string to a numeric if it is a string" do
52
+ @ms.to_numeric("a").should == "a"
53
+ end
54
+
55
+ it "should convert a hash of string numbers to numbers" do
56
+ h = @ms.values_to_numeric({"a" => "1"})
57
+ h["a"].should == 1
58
+ end
59
+
60
+ it "should convert a hash into a comma separated csv prefixed by time" do
61
+ h = { "a" => 1, "b" => 2, "c" => "foo" }
62
+ @ms.hash_to_csv(h).should == "2011-10-17 00:00:00,1,2,foo"
63
+ end
64
+
65
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mysampler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Brown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mysql
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: graphite
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: MySampler is a tool written in ruby to poll SHOW GLOBAL STATUS in MySQL
84
+ and output the values to either a CSV or graphite/carbon. The interval at which
85
+ the polling occurs can be specified and the output can be either the absolute or
86
+ relative values, so you can see change over time. If logging to CSV, the a date
87
+ stamp is appended to the CSV file and it is rotated hourly (to be configurable later).
88
+ email:
89
+ - aaron@9minutesnooze.com
90
+ executables:
91
+ - mysampler
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - ".gitignore"
96
+ - ".rvmrc"
97
+ - Gemfile
98
+ - README.md
99
+ - bin/mysampler
100
+ - lib/mysampler.rb
101
+ - lib/mysampler/client.rb
102
+ - lib/mysampler/file.rb
103
+ - lib/mysampler/processctl.rb
104
+ - lib/mysampler/version.rb
105
+ - mysampler.gemspec
106
+ - spec/tests.rb
107
+ homepage: https://github.com/9minutesnooze/mysampler
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.2.2
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: A utility that logs MySQL statistics to CSV or graphite.
131
+ test_files:
132
+ - spec/tests.rb