metricsd 0.1.0 → 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 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