monitoring 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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!
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -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()
@@ -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"
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
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
+