monitoring-client 0.2.0

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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in monitoring.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ monitoring (0.0.1)
5
+ json
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ json (1.5.1)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ monitoring!
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/monitoringd ADDED
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Monitoring
4
+ class Daemon
5
+
6
+ require "optparse"
7
+ require "thread"
8
+ require "socket"
9
+ require "json"
10
+ require "net/https"
11
+ require "uri"
12
+ require "timeout"
13
+
14
+ RC_OK = 0
15
+ RC_USAGE = 1
16
+ RC_KILLED = 2
17
+
18
+
19
+ def initialize()
20
+ @udp_port = 57475
21
+ @daemon_mode = false
22
+ @monitoring_endpoint = "http://metrics.trim.za.net"
23
+ @pid_file_fn = "/var/run/monitoringd.pid"
24
+ @sleep_period = 60
25
+ @stdout = "/dev/null"
26
+ @stderr = "/dev/null"
27
+ @alive = true
28
+ end
29
+
30
+
31
+ def parse_opts(argv)
32
+ optparser = OptionParser.new() do |o|
33
+ o.banner = "monitoringd [options]"
34
+
35
+ o.separator("")
36
+ o.separator("options")
37
+
38
+ o.on("-p", "--udp-port=PORT",
39
+ "Port to listen on",
40
+ "Default: #{@udp_port}") do |v|
41
+ @udp_port = Integer(v)
42
+ end
43
+
44
+ o.on("-f", "--forgeround",
45
+ "Run in forground",
46
+ "Default: #{!@daemon_mode}") do |v|
47
+ @daemon_mode = !v
48
+ end
49
+
50
+ o.on("--monitoring-endpoint=URI",
51
+ "Endpoint to post to",
52
+ "Default: " + @monitoring_endpoint.inspect()) do |v|
53
+ @monitoring_endpoint = v
54
+ end
55
+
56
+ o.on("--pid-file=FILE",
57
+ "Place to store our pid file",
58
+ "Default: " + @pid_file_fn.inspect()) do |v|
59
+ @pid_file_fn = v
60
+ end
61
+
62
+ o.on("--sleep-period=s",
63
+ "Time to sleep between posts",
64
+ "Default: #{@sleep_period}") do |v|
65
+ @sleep_period = Float(v)
66
+ end
67
+
68
+ o.on("--stdout=FILE",
69
+ "Place to write stdout to",
70
+ "Default: " + @stdout.inspect()) do |v|
71
+ @stdout = v
72
+ end
73
+
74
+ o.on("--stderr=FILE",
75
+ "Place to write stdout to",
76
+ "Default: " + @stderr.inspect()) do |v|
77
+ @stderr = v
78
+ end
79
+
80
+ o.separator("")
81
+ o.on_tail("-h", "--help", "You're reading it :-)") do
82
+ puts(o)
83
+ Kernel.exit(RC_USAGE)
84
+ end
85
+ end
86
+
87
+ optparser.parse(argv)
88
+ self
89
+ end
90
+
91
+ def run()
92
+ daemonize() if @daemon_mode
93
+
94
+ trap(:INT) do
95
+ if @alive
96
+ STDOUT.puts("SIGINT: going away gracefully")
97
+ @alive = false
98
+ else
99
+ STDOUT.puts("SIGINT (again): going away not so gracefully")
100
+ Kernel.exit!(RC_KILLED)
101
+ end
102
+ end
103
+
104
+ @samples = Queue.new()
105
+ server = Thread.new { collect_samples() }
106
+ uri = URI.parse(@monitoring_endpoint)
107
+ while @alive
108
+ samples_to_post = []
109
+ samples_to_post.push(@samples.pop()) until @samples.empty?
110
+ post_samples(uri, samples_to_post) unless samples_to_post.empty?
111
+ Kernel.sleep(@sleep_period)
112
+ end
113
+ #server.join()
114
+ end
115
+
116
+ def daemonize()
117
+ pid_file_fd = File.open(@pid_file_fn, "w")
118
+ pid_file_fd.flock(File::LOCK_EX | File::LOCK_NB) ||
119
+ die("Daemon already exists")
120
+
121
+ die("First fork failed") if (pid = Kernel.fork()) == -1
122
+ exit unless pid.nil?
123
+
124
+ Process.setsid
125
+ pid_file_fd.puts(Process.pid)
126
+
127
+ Dir.chdir("/")
128
+ File.umask(0000)
129
+ STDIN.reopen("/dev/null")
130
+ STDOUT.reopen(@stdout)
131
+ STDERR.reopen(@stderr)
132
+ end
133
+
134
+ def die(why)
135
+ STDERR.puts(why)
136
+ Kernel.exit!(1)
137
+ end
138
+
139
+ def collect_samples()
140
+ socket = UDPSocket.new
141
+ socket.bind(nil, @udp_port)
142
+ while @alive
143
+ begin
144
+ line = socket.gets()
145
+ JSON.parse(line).each do |sample|
146
+ @samples.push(sample)
147
+ end
148
+ rescue Exception => e;
149
+ STDOUT.puts("Could not parse " + line.inspect()) rescue nil
150
+ STDERR.puts([[e.class.name, e.message].join(": "),
151
+ e.backtrace.join("\n")].join("\n")) rescue nil
152
+ end
153
+ end
154
+ end
155
+
156
+ def post_samples(uri, samples)
157
+ Timeout.timeout(3) do
158
+ generic_http_post(uri, "/api/samples", nil, nil, { "samples" => samples.to_json })
159
+ end
160
+ rescue Timeout::Error => e
161
+ STDOUT.puts("Could not post sample (will requeue): " + samples.inspect()) rescue nil
162
+ STDERR.puts([[e.class.name, e.message].join(": "),
163
+ e.backtrace.join("\n")].join("\n")) rescue nil
164
+ samples.each do |s| @samples.push(s) end
165
+ rescue Exception => e
166
+ STDOUT.puts("Could not post sample (will not requeue): " + samples.inspect()) rescue nil
167
+ STDERR.puts([[e.class.name, e.message].join(": "),
168
+ e.backtrace.join("\n")].join("\n")) rescue nil
169
+ end
170
+
171
+ def generic_http_post(uri, path, user, pass, post_params={}, &block)
172
+ http = Net::HTTP.new(uri.host, uri.port)
173
+ http.use_ssl = uri.is_a?(URI::HTTPS)
174
+ req = Net::HTTP::Post.new(path)
175
+ req.set_form_data(post_params)
176
+ req.basic_auth(user, pass) if user and pass
177
+ resp = http.request(req)
178
+
179
+ raise RuntimeError, resp.class.name unless resp.is_a?(Net::HTTPSuccess)
180
+
181
+ if block_given?
182
+ block.call(resp.body)
183
+ else
184
+ resp.body
185
+ end
186
+ end
187
+
188
+ end
189
+ end
190
+
191
+ Monitoring::Daemon.new().parse_opts(ARGV).run()
data/lib/monitoring.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Monitoring
2
+ module Client
3
+ require "monitoring/client/version"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module Monitoring
2
+ module Client
3
+ class Metric
4
+
5
+ attr_accessor :units
6
+ attr_reader :value
7
+
8
+ def initialize(metric_name)
9
+ @value = nil
10
+ end
11
+
12
+
13
+ def value=(v)
14
+ raise(ArgumentError, "Must be a Fixnum or Float") unless
15
+ [Fixnum, Float].any? { |c| c === v }
16
+ @value = v
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ module Monitoring
2
+ module Client
3
+ class Profiler
4
+
5
+ require "json"
6
+ require "socket"
7
+ require "monitoring/client/routine"
8
+
9
+
10
+ def initialize(program_name)
11
+ @program_name = program_name
12
+ end
13
+
14
+
15
+ def routine(routine_name)
16
+ routine = Routine.new(routine_name)
17
+ yield(routine)
18
+ UDPSocket.new().send(routine.compile(@program_name).to_json + "\n",
19
+ 0, "localhost", 57475)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ module Monitoring
2
+ module Client
3
+ class Routine
4
+
5
+ require "monitoring/client/metric"
6
+
7
+
8
+ def initialize(routine_name)
9
+ @routine_name = routine_name
10
+ @metrics = Hash.new do |h, k| h[k] = Metric.new(k) end
11
+ end
12
+
13
+
14
+ def time(metric_name = "time")
15
+ t0 = Time.now.to_f
16
+ yield
17
+ @metrics[metric_name].value = Time.now.to_f - t0
18
+ @metrics[metric_name].units = "s"
19
+ end
20
+
21
+ def count(metric_name, increment_by, units = nil)
22
+ @metrics[metric_name].value = (@metrics[metric_name].value || 0) + increment_by
23
+ @metrics[metric_name].units = units
24
+ end
25
+
26
+ def compile(program_name)
27
+ @metrics.inject([]) do |compilation, metric_tuple|
28
+ metric_name, metric = metric_tuple
29
+ compilation.push({
30
+ "program_name" => program_name,
31
+ "routine_name" => @routine_name,
32
+ "metric_name" => metric_name,
33
+ "metric_units" => metric.units,
34
+ "sample_value" => metric.value,
35
+ })
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module Monitoring
2
+ module Client
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "monitoring/client/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "monitoring-client"
7
+ s.version = Monitoring::Client::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Marc Bowes"]
10
+ s.email = ["marcbowes+monitoring+client@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/monitoring-client"
12
+ s.summary = %q{Simple client & daemon to post metrics}
13
+ s.description = %q{Ruby client which uses UDP to communicate with a daemon which uses HTTP POST to communicate with a Web server}
14
+
15
+ s.rubyforge_project = "monitoring-client"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_runtime_dependency "json"
23
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monitoring-client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Marc Bowes
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-06 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: json
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: Ruby client which uses UDP to communicate with a daemon which uses HTTP POST to communicate with a Web server
36
+ email:
37
+ - marcbowes+monitoring+client@gmail.com
38
+ executables:
39
+ - monitoringd
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - .gitignore
46
+ - Gemfile
47
+ - Gemfile.lock
48
+ - Rakefile
49
+ - bin/monitoringd
50
+ - lib/monitoring.rb
51
+ - lib/monitoring/client/metric.rb
52
+ - lib/monitoring/client/profiler.rb
53
+ - lib/monitoring/client/routine.rb
54
+ - lib/monitoring/client/version.rb
55
+ - monitoring-client.gemspec
56
+ has_rdoc: true
57
+ homepage: http://rubygems.org/gems/monitoring-client
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project: monitoring-client
86
+ rubygems_version: 1.5.0
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Simple client & daemon to post metrics
90
+ test_files: []
91
+