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 +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +16 -0
- data/Rakefile +2 -0
- data/bin/monitoringd +191 -0
- data/lib/monitoring.rb +5 -0
- data/lib/monitoring/client/metric.rb +21 -0
- data/lib/monitoring/client/profiler.rb +24 -0
- data/lib/monitoring/client/routine.rb +41 -0
- data/lib/monitoring/client/version.rb +5 -0
- data/monitoring-client.gemspec +23 -0
- metadata +91 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/Rakefile
ADDED
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,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,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
|
+
|