metricsd 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .yardoc
6
+ doc
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ ## 0.2.0 (July 27, 2011)
2
+
3
+ Features:
4
+
5
+ - Added an option to enable or disable client
6
+
7
+ ## 0.1.0 (July 26, 2011)
8
+
9
+ Features:
10
+
11
+ - Complete MetricsD protocol implementation
12
+ - Allows to send multiple metrics in a single network packet
13
+ - Splits metrics to several packets of 250 bytes or less
14
+ - Allows to specify metrics parts separator (temporary solution until all the metrics will be migrated to "." separator)
15
+ - Logs network problems to logger and never throws errors like this.
16
+ - 100% test coverage
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :test => :spec
7
+ task :default => :spec
@@ -25,7 +25,7 @@ module Metricsd
25
25
  # 'db.pool.pending' => db_stats[:pending],
26
26
  # }, :group => 'doc_timestamp')
27
27
  #
28
- # You can specify message source using :source => 'src' option. In this case
28
+ # You can specify message source using <tt>:source => 'src'</tt> option. In this case
29
29
  # you will be able to see summary graphs and graphs per source:
30
30
  #
31
31
  # # Generate graphs for all tables, and each single table.
@@ -39,7 +39,7 @@ module Metricsd
39
39
  # # ... or you can pass an empty string with the same effect.
40
40
  # Metricsd::Client.record_success("hbase.reads", :source => '')
41
41
  #
42
- # You can group your metrics using :group option. In this case metrics will be
42
+ # You can group your metrics using <tt>:group</tt> option. In this case metrics will be
43
43
  # displayed together on the summary page.
44
44
  #
45
45
  # # Group metrics using :group option.
@@ -51,6 +51,19 @@ module Metricsd
51
51
  class << self
52
52
  # Record complete hit info. Time should be a floating point
53
53
  # number of seconds.
54
+ #
55
+ # It creates two metrics:
56
+ # * +your.metric.count+ with counts of failed and succeded events
57
+ # * +your.metric.time+ with time statistics
58
+ #
59
+ # @param [String] metric is the metric name (like app.docs.upload)
60
+ # @param [Boolean] is_success indicating whether request was successful.
61
+ # @param [Float] time floating point number of seconds.
62
+ # @param [Hash] opts options.
63
+ # @option opts [String] :sep ("_") separator used to add suffixes +count+ and +time+.
64
+ # @option opts [String] :group metrics group.
65
+ # @option opts [String] :source metric source.
66
+ #
54
67
  def record_hit(metric, is_success, time, opts = {})
55
68
  sep = opts[:sep] || opts[:separator] || '_'
56
69
  record_internal({
@@ -60,26 +73,51 @@ module Metricsd
60
73
  )
61
74
  end
62
75
 
63
- # Record success hit.
76
+ # Record succeded boolean event.
77
+ #
78
+ # It creates a single metric:
79
+ # * +your.metric.count+ with numbers of failed and succeded events
80
+ #
81
+ # @param [String] metric is the metric name (like app.docs.upload)
82
+ # @param [Hash] opts options.
83
+ # @option opts [String] :sep ("_") separator used to add suffixes +count+ and +time+.
84
+ # @option opts [String] :group metrics group.
85
+ # @option opts [String] :source metric source.
86
+ #
64
87
  def record_success(metric, opts = {})
65
88
  sep = opts[:sep] || opts[:separator] || '_'
66
- record_internal({
67
- "#{metric}#{sep}count" => 1
68
- }, opts
69
- )
89
+ record_internal({"#{metric}#{sep}count" => 1}, opts)
70
90
  end
71
91
 
72
- # Record success failure.
92
+ # Record failed boolean event.
93
+ #
94
+ # It creates a single metric:
95
+ # * +your.metric.count+ with numbers of failed and succeded events
96
+ #
97
+ # @param [String] metric is the metric name (like app.docs.upload)
98
+ # @param [Hash] opts options.
99
+ # @option opts [String] :sep ("_") separator used to add suffixes +count+ and +time+.
100
+ # @option opts [String] :group metrics group.
101
+ # @option opts [String] :source metric source.
102
+ #
73
103
  def record_failure(metric, opts = {})
74
104
  sep = opts[:sep] || opts[:separator] || '_'
75
- record_internal({
76
- "#{metric}#{sep}count" => -1
77
- }, opts
78
- )
105
+ record_internal({"#{metric}#{sep}count" => -1}, opts)
79
106
  end
80
107
 
81
108
  # Record timing info. Time should be a floating point
82
109
  # number of seconds.
110
+ #
111
+ # It creates a single metric:
112
+ # * +your.metric.time+ with time statistics
113
+ #
114
+ # @param [String] metric is the metric name (like app.docs.upload)
115
+ # @param [Float] time floating point number of seconds.
116
+ # @param [Hash] opts options.
117
+ # @option opts [String] :sep ("_") separator used to add suffixes +count+ and +time+.
118
+ # @option opts [String] :group metrics group.
119
+ # @option opts [String] :source metric source.
120
+ #
83
121
  def record_time(metric, time = nil, opts = {}, &block)
84
122
  opts, time = time, nil if Hash === time
85
123
  sep = opts[:sep] || opts[:separator] || '_'
@@ -87,31 +125,55 @@ module Metricsd
87
125
  raise ArgumentError, "You should pass a block if time is not given" unless block_given?
88
126
  time = Benchmark.measure(&block).real
89
127
  end
90
- record_internal({
91
- "#{metric}#{sep}time" => (time * 1000).round
92
- }, opts
93
- )
128
+ record_internal({"#{metric}#{sep}time" => (time * 1000).round}, opts)
94
129
  end
95
130
 
96
131
  # Record an integer value.
132
+ #
133
+ # It creates a single metric:
134
+ # * +your.metric+ with values statistics
135
+ #
136
+ # @param [String] metric is the metric name (like app.docs.upload)
137
+ # @param [Integer] value metric value.
138
+ # @param [Hash] opts options.
139
+ # @option opts [String] :sep ("_") separator used to add suffixes +count+ and +time+.
140
+ # @option opts [String] :group metrics group.
141
+ # @option opts [String] :source metric source.
142
+ #
97
143
  def record_value(metric, value, opts = {})
98
- record_internal({
99
- metric => value.round
100
- }, opts
101
- )
144
+ record_internal({metric => value.round}, opts)
102
145
  end
103
146
 
104
- # Record multiple values.
147
+ # Record multiple integer values.
148
+ #
149
+ # It creates a metric for each entry in +metrics+ Hash:
150
+ # * +your.metric+ with values statistics
151
+ #
152
+ # @param [Hash] metrics a +Hash+ that maps metrics names to their values.
153
+ # @param [Hash] opts options.
154
+ # @option opts [String] :sep ("_") separator used to add suffixes +count+ and +time+.
155
+ # @option opts [String] :group metrics group.
156
+ # @option opts [String] :source metric source.
157
+ #
158
+ # @example
159
+ # Metricsd::Client.record_values(
160
+ # 'db.pool.reserved' => db_stats[:reserved],
161
+ # 'db.pool.available' => db_stats[:available],
162
+ # 'db.pool.pending' => db_stats[:pending],
163
+ # )
164
+ #
105
165
  def record_values(metrics, opts = {})
106
166
  record_internal(metrics, opts)
107
167
  end
108
168
 
169
+ # Reset and re-establish connection.
109
170
  def reset_connection!
110
171
  @@socket = nil
111
172
  end
112
173
 
113
174
  private
114
175
 
176
+ # Returns a UDP socket used to send metrics to MetricsD.
115
177
  def collector_socket
116
178
  @@socket ||= begin
117
179
  @@socket = UDPSocket.new
@@ -121,6 +183,8 @@ module Metricsd
121
183
 
122
184
  # Send informating to the RRD collector daemon using UDP protocol.
123
185
  def record_internal(metrics, opts = {})
186
+ return unless Metricsd.enabled?
187
+
124
188
  opts = { :source => Metricsd.default_source }.update(opts)
125
189
  opts[:source] = Metricsd.source if opts[:source].empty?
126
190
 
@@ -128,6 +192,8 @@ module Metricsd
128
192
  send_in_packets Array(metrics).map { |arg| pack(arg[0], arg[1], opts) }.sort
129
193
  end
130
194
 
195
+ # Combines string representations of metrics into packets of 250 bytes and
196
+ # sends them to MetricsD.
131
197
  def send_in_packets(strings)
132
198
  msg = ''
133
199
  strings.each do |s|
@@ -145,6 +211,8 @@ module Metricsd
145
211
  safe_send(msg) if msg.size > 0
146
212
  end
147
213
 
214
+ # Sends a string to the MetricsD. Should never raise any network-specific
215
+ # exceptions, but log them instead, and silently return.
148
216
  def safe_send(msg)
149
217
  collector_socket.send(msg, 0)
150
218
  true
@@ -154,6 +222,8 @@ module Metricsd
154
222
  false
155
223
  end
156
224
 
225
+ # Packs metric into a string representation according to the MetricsD
226
+ # protocol.
157
227
  def pack(key, value, opts)
158
228
  key = "#{opts[:group]}$#{key}" unless opts[:group].nil? || opts[:group].empty?
159
229
  opts[:source].empty? ? "#{key}:#{value}" : "#{opts[:source]}@#{key}:#{value}"
@@ -1,3 +1,3 @@
1
1
  module Metricsd
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/metricsd.rb CHANGED
@@ -2,42 +2,77 @@ require 'socket'
2
2
  require 'logger'
3
3
  require 'benchmark'
4
4
 
5
+ # Pure Ruby client library for MetricsD server.
5
6
  module Metricsd
6
7
  class << self
8
+ # Gets the MetricsD server host. Default is "127.0.0.1".
7
9
  def server_host
8
10
  @@server_host
9
11
  end
10
12
 
13
+ # Sets the MetricsD server host.
11
14
  def server_host=(host)
12
15
  @@server_host = host
13
16
  Client.reset_connection!
14
17
  end
15
18
 
19
+ # Gets the MetricsD server port. Default is 6311.
16
20
  def server_port
17
21
  @@server_port
18
22
  end
19
23
 
24
+ # Sets the MetricsD server port.
20
25
  def server_port=(port)
21
26
  @@server_port = Integer(port)
22
27
  Client.reset_connection!
23
28
  end
24
29
 
30
+ # Get the value indicating whether Metricsd is enabled.
31
+ def enabled?
32
+ !!@@enabled
33
+ end
34
+ alias :enabled :enabled?
35
+
36
+ # Sets the value indicating whether Metricsd is enabled.
37
+ def enabled=(enabled)
38
+ @@enabled = !!enabled
39
+ end
40
+
41
+ # Enables Metricsd client.
42
+ def enable!
43
+ @@enabled = true
44
+ end
45
+
46
+ # Disables Metricsd client.
47
+ def disable!
48
+ @@enabled = false
49
+ end
50
+
51
+ # Gets the source used to record host-specific metrics. Default is the
52
+ # first part of the hostname (e.g. "test" for "test.host.com").
25
53
  def source
26
54
  @@source || metricsd.default_source
27
55
  end
28
56
 
57
+ # Sets the source for host-specific metrics.
29
58
  def source=(source)
30
59
  @@source = source
31
60
  end
32
61
 
62
+ # Gets the default source for all metrics. If nil or empty string — all
63
+ # metrics will be host-specific (MetricsD server will generate per-host
64
+ # graphs in addition to summary graph for all hosts for each metric).
65
+ # Default is "all".
33
66
  def default_source
34
67
  @@default_source
35
68
  end
36
69
 
70
+ # Sets the default source for all metrics.
37
71
  def default_source=(source)
38
72
  @@default_source = source
39
73
  end
40
74
 
75
+ # Gets the logger used to output errors or warnings.
41
76
  def logger
42
77
  @@logger ||= if defined?(Rails)
43
78
  Rails.logger
@@ -48,13 +83,16 @@ module Metricsd
48
83
  end
49
84
  end
50
85
 
86
+ # Sets the logger used to output errors or warnings.
51
87
  def logger=(logger)
52
88
  @@logger = logger
53
89
  end
54
90
 
91
+ # Resets all values to their default state (mostly for testing purpose).
55
92
  def reset_defaults!
56
93
  @@server_host = '127.0.0.1'
57
94
  @@server_port = 6311
95
+ @@enabled = true
58
96
  @@source = Socket.gethostname[/^([^.]+)/, 1]
59
97
  @@default_source = 'all'
60
98
  @@logger = nil
data/metricsd.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.summary = %q{Client library for MetricsD server}
12
12
  s.description = %q{}
13
13
 
14
+ s.add_development_dependency 'rake'
14
15
  s.add_development_dependency 'rspec'
15
16
  s.add_development_dependency 'guard-rspec'
16
17
  s.add_development_dependency 'rb-fsevent'
data/spec/client_spec.rb CHANGED
@@ -5,7 +5,9 @@ describe Metricsd::Client do
5
5
  before :each do
6
6
  @socket = mock('Socket')
7
7
  Metricsd::Client.stub!(:collector_socket => @socket)
8
- Metricsd.stub!(:source => 'test')
8
+ # Global settings
9
+ Metricsd.reset_defaults!
10
+ Metricsd.source = 'test'
9
11
  end
10
12
 
11
13
  describe 'connection' do
@@ -15,6 +17,12 @@ describe Metricsd::Client do
15
17
  Metricsd.logger.should_receive(:error).at_least(1) # stacktrace
16
18
  Metricsd::Client.record_value('custom.metric', 5)
17
19
  end
20
+
21
+ it 'should not send anything to the socket if disabled' do
22
+ Metricsd.disable!
23
+ @socket.should_not_receive(:send)
24
+ Metricsd::Client.record_value('custom.metric', 5)
25
+ end
18
26
  end
19
27
 
20
28
  describe '.record_hit' do
@@ -28,6 +28,11 @@ describe Metricsd do
28
28
  it 'should create logger' do
29
29
  Metricsd.logger.should be_a(Logger)
30
30
  end
31
+
32
+ it 'should be enabled' do
33
+ Metricsd.enabled?.should be_true
34
+ Metricsd.enabled.should be_true
35
+ end
31
36
  end
32
37
 
33
38
  context 'setters' do
@@ -47,6 +52,20 @@ describe Metricsd do
47
52
  }.to raise_error
48
53
  end
49
54
 
55
+ it 'should allow to enabled and disable client' do
56
+ Metricsd.enabled = false
57
+ Metricsd.should_not be_enabled
58
+
59
+ Metricsd.enable!
60
+ Metricsd.should be_enabled
61
+
62
+ Metricsd.disable!
63
+ Metricsd.should_not be_enabled
64
+
65
+ Metricsd.enabled = true
66
+ Metricsd.should be_enabled
67
+ end
68
+
50
69
  it 'should allow to change source' do
51
70
  Metricsd.source = 'test-host'
52
71
  Metricsd.source.should == 'test-host'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metricsd
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Dmytro Shteflyuk
@@ -19,7 +19,7 @@ date: 2011-07-27 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: rspec
22
+ name: rake
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
@@ -33,7 +33,7 @@ dependencies:
33
33
  type: :development
34
34
  version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency
36
- name: guard-rspec
36
+ name: rspec
37
37
  prerelease: false
38
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
39
  none: false
@@ -47,7 +47,7 @@ dependencies:
47
47
  type: :development
48
48
  version_requirements: *id002
49
49
  - !ruby/object:Gem::Dependency
50
- name: rb-fsevent
50
+ name: guard-rspec
51
51
  prerelease: false
52
52
  requirement: &id003 !ruby/object:Gem::Requirement
53
53
  none: false
@@ -61,7 +61,7 @@ dependencies:
61
61
  type: :development
62
62
  version_requirements: *id003
63
63
  - !ruby/object:Gem::Dependency
64
- name: growl
64
+ name: rb-fsevent
65
65
  prerelease: false
66
66
  requirement: &id004 !ruby/object:Gem::Requirement
67
67
  none: false
@@ -74,6 +74,20 @@ dependencies:
74
74
  version: "0"
75
75
  type: :development
76
76
  version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: growl
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ type: :development
90
+ version_requirements: *id005
77
91
  description: ""
78
92
  email:
79
93
  - kpumuk@kpumuk.info
@@ -86,6 +100,7 @@ extra_rdoc_files: []
86
100
  files:
87
101
  - .gitignore
88
102
  - .rvmrc
103
+ - CHANGELOG.md
89
104
  - Gemfile
90
105
  - Guardfile
91
106
  - README.md