metriks 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ require 'logger'
2
+
3
+ module Metriks::Reporter
4
+ class Logger
5
+ def initialize(options = {})
6
+ @registry = options[:registry] || Metriks::Registry.default
7
+ @logger = options[:logger] || ::Logger.new(STDOUT)
8
+ @log_level = options[:log_level] || ::Logger::INFO
9
+ @prefix = options[:prefix] || 'metriks:'
10
+ @interval = options[:interval] || 60
11
+ @on_errror = options[:on_error] || proc { |ex| }
12
+ end
13
+
14
+ def start
15
+ @thread ||= Thread.new do
16
+ loop do
17
+ sleep @interval
18
+
19
+ Thread.new do
20
+ begin
21
+ write
22
+ rescue Exception => ex
23
+ @on_error[ex] rescue nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def stop
31
+ @thread.kill if @thread
32
+ @thread = nil
33
+ end
34
+
35
+ def write
36
+ @registry.each do |name, metric|
37
+ case metric
38
+ when Metriks::Meter
39
+ log_metric name, 'meter', metric, [
40
+ :count, :one_minute_rate, :five_minute_rate,
41
+ :fifteen_minute_rate, :mean_rate
42
+ ]
43
+ when Metriks::Counter
44
+ log_metric name, 'counter', metric, [
45
+ :count
46
+ ]
47
+ when Metriks::UtilizationTimer
48
+ log_metric name, 'utilization_timer', metric, [
49
+ :count, :one_minute_rate, :five_minute_rate,
50
+ :fifteen_minute_rate, :mean_rate,
51
+ :min, :max, :mean, :stddev,
52
+ :one_minute_utilization, :five_minute_utilization,
53
+ :fifteen_minute_utilization, :mean_utilization,
54
+ ]
55
+ when Metriks::Timer
56
+ log_metric name, 'timer', metric, [
57
+ :count, :one_minute_rate, :five_minute_rate,
58
+ :fifteen_minute_rate, :mean_rate,
59
+ :min, :max, :mean, :stddev
60
+ ]
61
+ end
62
+ end
63
+ end
64
+
65
+ def extract_from_metric(metric, *keys)
66
+ keys.flatten.collect do |key|
67
+ [ { key => metric.send(key) } ]
68
+ end
69
+ end
70
+
71
+ def log_metric(name, type, metric, *keys)
72
+ message = []
73
+
74
+ message << @prefix if @prefix
75
+ message << { :time => Time.now.to_i }
76
+
77
+ message << { :name => name }
78
+ message << { :type => type }
79
+ message += extract_from_metric(metric, keys)
80
+
81
+ @logger.add(@log_level, format_message(message))
82
+ end
83
+
84
+ def format_message(args)
85
+ args.map do |arg|
86
+ case arg
87
+ when Hash then arg.map { |name, value| "#{name}=#{format_message([value])}" }
88
+ when Array then format_message(arg)
89
+ else arg
90
+ end
91
+ end.join(' ')
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,59 @@
1
+ module Metriks::Reporter
2
+ class ProcTitle
3
+ def initialize(options = {})
4
+ @interval = options[:interval] || 5
5
+ @rounding = options[:rounding] || 1
6
+ @on_errror = options[:on_error] || proc { |ex| }
7
+
8
+ @prefix = $0.dup
9
+ @metrics = []
10
+ end
11
+
12
+ def add(name, suffix = nil, &block)
13
+ @metrics << [ name, suffix, block ]
14
+ end
15
+
16
+ def empty?
17
+ @metrics.empty?
18
+ end
19
+
20
+ def start
21
+ @thread ||= Thread.new do
22
+ loop do
23
+ begin
24
+ unless @metrics.empty?
25
+ title = generate_title
26
+ if title && !title.empty?
27
+ $0 = "#{@prefix} #{title}"
28
+ end
29
+ end
30
+ rescue Exception => ex
31
+ @on_errror[ex] rescue nil
32
+ end
33
+ sleep @interval
34
+ end
35
+ end
36
+ end
37
+
38
+ def stop
39
+ @thread.kill if @thread
40
+ @thread = nil
41
+ end
42
+
43
+ protected
44
+ def generate_title
45
+ @metrics.collect do |name, suffix, block|
46
+ val = block.call
47
+ val = "%.#{@rounding}f" % val if val.is_a?(Float)
48
+
49
+ if suffix == '%'
50
+ "#{name}: #{val}#{suffix}"
51
+ elsif suffix
52
+ "#{name}: #{val}/#{suffix}"
53
+ else
54
+ "#{name}: #{val}"
55
+ end
56
+ end.join(' ')
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,60 @@
1
+ require 'atomic'
2
+
3
+ module Metriks
4
+ class SimpleMovingAverage
5
+ INTERVAL = 5.0
6
+ SECONDS_PER_MINUTE = 60.0
7
+
8
+ ONE_MINUTE = 1
9
+ FIVE_MINUTES = 5
10
+ FIFTEEN_MINUTES = 15
11
+
12
+ def self.new_m1
13
+ new(ONE_MINUTE * SECONDS_PER_MINUTE, INTERVAL)
14
+ end
15
+
16
+ def self.new_m5
17
+ new(FIVE_MINUTES * SECONDS_PER_MINUTE, INTERVAL)
18
+ end
19
+
20
+ def self.new_m15
21
+ new(FIFTEEN_MINUTES * SECONDS_PER_MINUTE, INTERVAL)
22
+ end
23
+
24
+ def initialize(duration, interval)
25
+ @interval = interval
26
+ @duration = duration
27
+
28
+ @values = Array.new((duration / interval).to_i) { Atomic.new(nil) }
29
+ @index = Atomic.new(0)
30
+ end
31
+
32
+ def clear
33
+ @values.each do |value|
34
+ value.value = nil
35
+ end
36
+ @index.value = 0
37
+ end
38
+
39
+ def update(value)
40
+ @values[@index.value].update { |v| v ? v + value : value }
41
+ end
42
+
43
+ def tick
44
+ @index.update { |v| v < @values.length - 1 ? v + 1 : 0 }
45
+ end
46
+
47
+ def rate
48
+ num, count = 0.0, 0.0
49
+
50
+ @values.each do |value|
51
+ if v = value.value
52
+ num += v
53
+ count += 1
54
+ end
55
+ end
56
+
57
+ num / count / @interval.to_f
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,93 @@
1
+ require 'atomic'
2
+ require 'hitimes'
3
+
4
+ require 'metriks/meter'
5
+ require 'metriks/histogram'
6
+
7
+ module Metriks
8
+ class Timer
9
+ class Context
10
+ def initialize(timer)
11
+ @timer = timer
12
+ @interval = Hitimes::Interval.now
13
+ end
14
+
15
+ def stop
16
+ @interval.stop
17
+ @timer.update(@interval.duration)
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ @meter = Metriks::Meter.new
23
+ @histogram = Metriks::Histogram.new_uniform
24
+ end
25
+
26
+ def clear
27
+ @meter.clear
28
+ @histogram.clear
29
+ end
30
+
31
+ def update(duration)
32
+ if duration >= 0
33
+ @meter.mark
34
+ @histogram.update(duration)
35
+ end
36
+ end
37
+
38
+ def time(callable = nil, &block)
39
+ callable ||= block
40
+ context = Context.new(self)
41
+
42
+ if callable.nil?
43
+ return context
44
+ end
45
+
46
+ begin
47
+ return callable.call
48
+ ensure
49
+ context.stop
50
+ end
51
+ end
52
+
53
+ def count
54
+ @histogram.count
55
+ end
56
+
57
+ def one_minute_rate
58
+ @meter.one_minute_rate
59
+ end
60
+
61
+ def five_minute_rate
62
+ @meter.five_minute_rate
63
+ end
64
+
65
+ def fifteen_minute_rate
66
+ @meter.fifteen_minute_rate
67
+ end
68
+
69
+ def mean_rate
70
+ @meter.mean_rate
71
+ end
72
+
73
+ def min
74
+ @histogram.min
75
+ end
76
+
77
+ def max
78
+ @histogram.max
79
+ end
80
+
81
+ def mean
82
+ @histogram.mean
83
+ end
84
+
85
+ def stddev
86
+ @histogram.stddev
87
+ end
88
+
89
+ def stop
90
+ @meter.stop
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,35 @@
1
+ require 'atomic'
2
+
3
+ module Metriks
4
+ class UniformSample
5
+ def initialize(reservoir_size)
6
+ @values = Array.new(reservoir_size, 0)
7
+ @count = Atomic.new(0)
8
+ end
9
+
10
+ def clear
11
+ @values.length.times do |idx|
12
+ @values[idx] = 0
13
+ end
14
+ @count.value = 0
15
+ end
16
+
17
+ def size
18
+ count = @count.value
19
+ count > @values.length ? @values.length : count
20
+ end
21
+
22
+ def update(value)
23
+ new_count = @count.update { |v| v + 1 }
24
+
25
+ if new_count <= @values.length
26
+ @values[new_count - 1] = value
27
+ else
28
+ idx = rand(new_count)
29
+ if idx < @values.length
30
+ @values[idx] = value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ require 'metriks/timer'
2
+
3
+ module Metriks
4
+ class UtilizationTimer < Metriks::Timer
5
+ def initialize
6
+ super
7
+ @duration_meter = Metriks::Meter.new
8
+ end
9
+
10
+ def clear
11
+ super
12
+ @duration_meter.clear
13
+ end
14
+
15
+ def update(duration)
16
+ super
17
+ if duration >= 0
18
+ @duration_meter.mark(duration)
19
+ end
20
+ end
21
+
22
+ def one_minute_utilization
23
+ @duration_meter.one_minute_rate
24
+ end
25
+
26
+ def five_minute_utilization
27
+ @duration_meter.five_minute_rate
28
+ end
29
+
30
+ def fifteen_minute_utilization
31
+ @duration_meter.fifteen_minute_rate
32
+ end
33
+
34
+ def mean_utilization
35
+ @duration_meter.mean_rate
36
+ end
37
+
38
+ def stop
39
+ super
40
+ @duration_meter.stop
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,90 @@
1
+ ## This is the rakegem gemspec template. Make sure you read and understand
2
+ ## all of the comments. Some sections require modification, and others can
3
+ ## be deleted if you don't need them. Once you understand the contents of
4
+ ## this file, feel free to delete any comments that begin with two hash marks.
5
+ ## You can find comprehensive Gem::Specification documentation, at
6
+ ## http://docs.rubygems.org/read/chapter/20
7
+ Gem::Specification.new do |s|
8
+ s.specification_version = 2 if s.respond_to? :specification_version=
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.rubygems_version = '1.3.5'
11
+
12
+ ## Leave these as is they will be modified for you by the rake gemspec task.
13
+ ## If your rubyforge_project name is different, then edit it and comment out
14
+ ## the sub! line in the Rakefile
15
+ s.name = 'metriks'
16
+ s.version = '0.8.1'
17
+ s.date = '2012-02-27'
18
+
19
+ ## Make sure your summary is short. The description may be as long
20
+ ## as you like.
21
+ s.summary = "An experimental metrics client"
22
+ s.description = "An experimental metrics client."
23
+
24
+ ## List the primary authors. If there are a bunch of authors, it's probably
25
+ ## better to set the email to an email list or something. If you don't have
26
+ ## a custom homepage, consider using your GitHub URL or the like.
27
+ s.authors = ["Eric Lindvall"]
28
+ s.email = 'eric@sevenscale.com'
29
+ s.homepage = 'https://github.com/eric/metriks_client'
30
+
31
+ ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
32
+ ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
33
+ s.require_paths = %w[lib]
34
+
35
+ ## If your gem includes any executables, list them here.
36
+ # s.executables = ["name"]
37
+
38
+ ## Specify any RDoc options here. You'll want to add your README and
39
+ ## LICENSE files to the extra_rdoc_files list.
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.extra_rdoc_files = %w[README.md LICENSE]
42
+
43
+ ## List your runtime dependencies here. Runtime dependencies are those
44
+ ## that are needed for an end user to actually USE your code.
45
+ s.add_dependency('atomic', ["~> 1.0"])
46
+ s.add_dependency('hitimes', [ "~> 1.1"])
47
+
48
+ ## List your development dependencies here. Development dependencies are
49
+ ## those that are only needed during development
50
+ s.add_development_dependency('tomdoc', ["~> 0.2"])
51
+
52
+ ## Leave this section as-is. It will be automatically generated from the
53
+ ## contents of your Git repository via the gemspec task. DO NOT REMOVE
54
+ ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
55
+ # = MANIFEST =
56
+ s.files = %w[
57
+ Gemfile
58
+ LICENSE
59
+ README.md
60
+ Rakefile
61
+ lib/metriks.rb
62
+ lib/metriks/counter.rb
63
+ lib/metriks/ewma.rb
64
+ lib/metriks/histogram.rb
65
+ lib/metriks/meter.rb
66
+ lib/metriks/registry.rb
67
+ lib/metriks/reporter/logger.rb
68
+ lib/metriks/reporter/proc_title.rb
69
+ lib/metriks/simple_moving_average.rb
70
+ lib/metriks/timer.rb
71
+ lib/metriks/uniform_sample.rb
72
+ lib/metriks/utilization_timer.rb
73
+ metriks.gemspec
74
+ test/counter_test.rb
75
+ test/histogram_test.rb
76
+ test/logger_reporter_test.rb
77
+ test/meter_test.rb
78
+ test/metriks_test.rb
79
+ test/proc_title_reporter_test.rb
80
+ test/registry_test.rb
81
+ test/test_helper.rb
82
+ test/timer_test.rb
83
+ test/utilization_timer_test.rb
84
+ ]
85
+ # = MANIFEST =
86
+
87
+ ## Test files will be grabbed from the file list. Make sure the path glob
88
+ ## matches what you actually use.
89
+ s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
90
+ end