afstatsd 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/example/example.rb CHANGED
@@ -1,69 +1,69 @@
1
- require 'afstatsd'
2
-
3
- #$statsd = Statsd.new 'statsd_server.my_company.com', 8125, 20
4
-
5
- $statsd = Statsd.new # use defaults
6
- $statsd.namespace = 'test.ruby'
7
-
8
-
9
- $statsd.increment 'counter1'
10
- $statsd.increment 'counter1'
11
- $statsd.decrement 'counter1' #counters accumulate
12
-
13
- $statsd.gauge 'gauge1', 1024
14
- $statsd.gauge 'gauge1', 1025
15
- $statsd.gauge 'gauge1', 1026
16
- $statsd.gauge 'gauge1', 1027
17
- $statsd.gauge 'gauge1', 1028 # gauges get averaged when aggregated
18
-
19
- $statsd.time('timing1' ){sleep 0.01}
20
- $statsd.time('timing1' ){sleep 0.02}
21
- $statsd.time('timing1' ){sleep 0.03}
22
- $statsd.time('timing1' ){sleep 0.04} # timings get averaged when aggregated
23
-
24
-
25
- =begin
26
-
27
- 100.times do
28
- #$statsd.increment 'sampled', 0.1, 'sampled'
29
- $statsd.increment 'sampled'
30
- end
31
-
32
- $statsd.set 'set1', 1099, "ez"
33
-
34
- for i in 10..19 do
35
- $statsd.increment "counter#{i}" # create a group of counters
36
- end
37
-
38
- 1000.times do
39
- $statsd.increment 'fast' # don't do this if aggregation is off
40
- end
41
-
42
- # In this test program, this will give the aggregator time to run.
43
- 15.times do
44
- sleep 2
45
- $statsd.increment 'slow'
46
- end
47
-
48
- =end
49
-
50
- =begin
51
- # test for thread safety
52
- threads = []
53
- start = Time.now
54
- for i in 0..9 do
55
- threads << Thread.new(i) do |j|
56
- start = Time.now
57
- 1000000.times do
58
- $statsd.increment 'inthethread'
59
- # sleep(0.01)
60
- end
61
- puts "thread #{j} says: I took #{((Time.now - start)*1000).round} ms"
62
- end
63
- end
64
- threads.each { |t| t.join }
65
-
66
- puts "total time: #{((Time.now - start)*1000).round} ms"
67
- =end
68
-
69
- puts "#{$statsd.dropped} messages dropped"
1
+ require 'afstatsd'
2
+
3
+ #$statsd = Statsd.new 'statsd_server.my_company.com', 8125, 20
4
+
5
+ $statsd = Statsd.new # use defaults
6
+ $statsd.namespace = 'test.ruby'
7
+
8
+
9
+ $statsd.increment 'counter1'
10
+ $statsd.increment 'counter1'
11
+ $statsd.decrement 'counter1' #counters accumulate
12
+
13
+ $statsd.gauge 'gauge1', 1024
14
+ $statsd.gauge 'gauge1', 1025
15
+ $statsd.gauge 'gauge1', 1026
16
+ $statsd.gauge 'gauge1', 1027
17
+ $statsd.gauge 'gauge1', 1028 # gauges get averaged when aggregated
18
+
19
+ $statsd.time('timing1' ){sleep 0.01}
20
+ $statsd.time('timing1' ){sleep 0.02}
21
+ $statsd.time('timing1' ){sleep 0.03}
22
+ $statsd.time('timing1' ){sleep 0.04} # timings get averaged when aggregated
23
+
24
+
25
+ =begin
26
+
27
+ 100.times do
28
+ #$statsd.increment 'sampled', 0.1, 'sampled'
29
+ $statsd.increment 'sampled'
30
+ end
31
+
32
+ $statsd.set 'set1', 1099, "ez"
33
+
34
+ for i in 10..19 do
35
+ $statsd.increment "counter#{i}" # create a group of counters
36
+ end
37
+
38
+ 1000.times do
39
+ $statsd.increment 'fast' # don't do this if aggregation is off
40
+ end
41
+
42
+ # In this test program, this will give the aggregator time to run.
43
+ 15.times do
44
+ sleep 2
45
+ $statsd.increment 'slow'
46
+ end
47
+
48
+ =end
49
+
50
+ =begin
51
+ # test for thread safety
52
+ threads = []
53
+ start = Time.now
54
+ for i in 0..9 do
55
+ threads << Thread.new(i) do |j|
56
+ start = Time.now
57
+ 1000000.times do
58
+ $statsd.increment 'inthethread'
59
+ # sleep(0.01)
60
+ end
61
+ puts "thread #{j} says: I took #{((Time.now - start)*1000).round} ms"
62
+ end
63
+ end
64
+ threads.each { |t| t.join }
65
+
66
+ puts "total time: #{((Time.now - start)*1000).round} ms"
67
+ =end
68
+
69
+ puts "#{$statsd.dropped} messages dropped"
@@ -44,18 +44,15 @@ class GMetric < Metric
44
44
  @name = name
45
45
  @value = value
46
46
  @message = msg
47
- @count = 1
48
47
  end
49
48
 
50
49
  def aggregate(value)
51
- @value += value #average
52
- @count += 1
50
+ @value = value #overwrite
53
51
  end
54
52
 
55
53
  def to_s
56
- avg = @value / @count
57
54
  if @message == "" then m = "" else m = "|#{@message}" end
58
- "#{name}:#{avg}|g#{m}"
55
+ "#{name}:#{@value}|g#{m}"
59
56
  end
60
57
 
61
58
  end
data/lib/afstatsd.rb CHANGED
@@ -1,281 +1,281 @@
1
- require 'socket'
2
- require 'forwardable'
3
- require 'rubygems'
4
- require 'posix_mq'
5
- require 'afstatsd/statsd_metrics'
6
- require 'afstatsd/statsd_aggregator'
7
- require 'monitor'
8
- require 'fcntl'
9
-
10
- # = Statsd: A Statsd client (https://github.com/etsy/statsd)
11
- #
12
- # @example Set up a global Statsd client for a server on localhost:9125,
13
- # aggregate 20 seconds worth of metrics
14
- # $statsd = Statsd.new 'localhost', 8125, 20
15
- # @example Send some stats
16
- # $statsd.increment 'garets'
17
- # $statsd.timing 'glork', 320
18
- # $statsd.gauge 'bork', 100
19
- # @example Use {#time} to time the execution of a block
20
- # $statsd.time('account.activate') { @account.activate! }
21
- # @example Create a namespaced statsd client and increment 'account.activate'
22
- # statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
23
- # statsd.increment 'activate'
24
- #
25
- # Statsd instances are thread safe for general usage, by using a thread local
26
- # UDPSocket and carrying no state. The attributes are stateful, and are not
27
- # mutexed, it is expected that users will not change these at runtime in
28
- # threaded environments. If users require such use cases, it is recommend that
29
- # users either mutex around their Statsd object, or create separate objects for
30
- # each namespace / host+port combination.
31
- class Statsd
32
-
33
- # A namespace to prepend to all statsd calls.
34
- attr_reader :namespace
35
-
36
- # StatsD host. Defaults to 127.0.0.1. Only used with UDP transport
37
- attr_reader :host
38
-
39
- # StatsD port. Defaults to 8125. Only used with UDP transport
40
- attr_reader :port
41
-
42
- # StatsD namespace prefix, generated from #namespace
43
- attr_reader :prefix
44
-
45
- # a postfix to append to all metrics
46
- attr_reader :postfix
47
-
48
- # count of messages that were dropped due to transmit error
49
- attr_reader :dropped
50
-
51
- class << self
52
- # Set to a standard logger instance to enable debug logging.
53
- attr_accessor :logger
54
- end
55
-
56
- # @param [String] host your statsd host
57
- # @param [Integer] port your statsd port
58
- # @param [Integer] interval for aggregatore
59
- def initialize(host = '127.0.0.1', port = 8125, interval = 20)
60
- self.host, self.port = host, port
61
- @prefix = nil
62
- @postfix = nil
63
- @aggregator = StatsdAggregator.new(interval)
64
- set_transport :mq_transport
65
- self.aggregating = true unless interval == 0
66
- @dropped = 0
67
- end
68
-
69
- # @param [method] The ruby symbol for the method that gets called to send
70
- # one metric to the server. eg: set_transport :udp_transport
71
- def set_transport(transport)
72
- @transport = method(transport)
73
- @aggregator.transport = @transport # aggregator needs to know
74
- end
75
-
76
- # @param [Boolean] Turn aggregation on or off
77
- def aggregating= (should_aggregate)
78
- if should_aggregate
79
- @aggregator.start(@transport)
80
- else
81
- @aggregator.stop
82
- end
83
- end
84
-
85
- # is the aggregator running?
86
- def aggregating
87
- @aggregator.running
88
- end
89
-
90
- # @attribute [w] namespace
91
- # Writes are not thread safe.
92
- def namespace=(namespace)
93
- @namespace = namespace
94
- @prefix = "#{namespace}."
95
- end
96
-
97
- # @attribute [w] postfix
98
- # A value to be appended to the stat name after a '.'. If the value is
99
- # blank then the postfix will be reset to nil (rather than to '.').
100
- def postfix=(pf)
101
- case pf
102
- when nil, false, '' then @postfix = nil
103
- else @postfix = ".#{pf}"
104
- end
105
- end
106
-
107
- # @attribute [w] host
108
- # Writes are not thread safe.
109
- def host=(host)
110
- @host = host || '127.0.0.1'
111
- end
112
-
113
- # @attribute [w] port
114
- # Writes are not thread safe.
115
- def port=(port)
116
- @port = port || 8125
117
- end
118
-
119
- # Sends an increment (count = 1) for the given stat to the statsd server.
120
- #
121
- # @param [String] stat stat name
122
- # @param [Numeric] sample_rate sample rate, 1 for always
123
- # @param [String] optional note (AppFirst extension to StatsD)
124
- # @see #count
125
- def increment(stat, sample_rate=1, note="")
126
- count stat, 1, sample_rate, note
127
- end
128
-
129
- # Sends a decrement (count = -1) for the given stat to the statsd server.
130
- #
131
- # @param [String] stat stat name
132
- # @param [Numeric] sample_rate sample rate, 1 for always
133
- # @param [String] optional note (AppFirst extension to StatsD)
134
- # @see #count
135
- def decrement(stat, sample_rate=1, note="")
136
- count stat, -1, sample_rate, note
137
- end
138
-
139
- # Sends an arbitrary count for the given stat to the statsd server.
140
- #
141
- # @param [String] stat stat name
142
- # @param [Integer] count count
143
- # @param [Numeric] sample_rate sample rate, 1 for always
144
- # @param [String] optional note (AppFirst extension to StatsD)
145
- def count(stat, count, sample_rate=1, note="")
146
- if sample_rate == 1 or rand < sample_rate
147
- send_metric StatsdMetrics::CMetric.new(expand_name(stat), count, sample_rate, note)
148
- end
149
- end
150
-
151
- # Sends an arbitary gauge value for the given stat to the statsd server.
152
- #
153
- # This is useful for recording things like available disk space,
154
- # memory usage, and the like, which have different semantics than
155
- # counters.
156
- #
157
- # @param [String] stat stat name.
158
- # @param [Numeric] value gauge value.
159
- # @param [String] optional note (AppFirst extension to StatsD)
160
- # @example Report the current user count:
161
- # $statsd.gauge('user.count', User.count)
162
- def gauge(stat, value, note="")
163
- send_metric StatsdMetrics::GMetric.new(expand_name(stat), value, note)
164
- end
165
-
166
- # Sends an arbitary set value for the given stat to the statsd server.
167
- #
168
- # This is for recording counts of unique events, which are useful to
169
- # see on graphs to correlate to other values. For example, a deployment
170
- # might get recorded as a set, and be drawn as annotations on a CPU history
171
- # graph.
172
- #
173
- # @param [String] stat stat name.
174
- # @param [Numeric] value event value.
175
- # @param [String] optional note (AppFirst extension to StatsD)
176
- # @example Report a deployment happening:
177
- # $statsd.set('deployment', DEPLOYMENT_EVENT_CODE)
178
- def set(stat, value, note="")
179
- send_metric StatsdMetrics::SMetric.new(expand_name(stat), value, note)
180
- end
181
-
182
- # Sends a timing (in ms) for the given stat to the statsd server. The
183
- # sample_rate determines what percentage of the time this report is sent. The
184
- # statsd server then uses the sample_rate to correctly track the average
185
- # timing for the stat.
186
- #
187
- # @param [String] stat stat name
188
- # @param [Integer] ms timing in milliseconds
189
- # @param [Numeric] sample_rate sample rate, 1 for always
190
- # @param [String] optional note (AppFirst extension to StatsD)
191
- def timing(stat, ms, sample_rate=1, note="")
192
- if sample_rate == 1 or rand < sample_rate
193
- send_metric StatsdMetrics::TMetric.new(expand_name(stat), ms, sample_rate, note)
194
- end
195
- end
196
-
197
- # Reports execution time of the provided block using {#timing}.
198
- #
199
- # @param [String] stat stat name
200
- # @param [Numeric] sample_rate sample rate, 1 for always
201
- # @param [String] optional note (AppFirst extension to StatsD)
202
- # @yield The operation to be timed
203
- # @see #timing
204
- # @example Report the time (in ms) taken to activate an account
205
- # $statsd.time('account.activate') { @account.activate! }
206
- def time(stat, sample_rate=1, note="")
207
- start = Time.now
208
- result = yield
209
- timing(stat, ((Time.now - start) * 1000).round, sample_rate, note)
210
- result
211
- end
212
-
213
- protected
214
-
215
- def send_metric(metric)
216
- # All the metric types above funnel to here. We will send or aggregate.
217
- if aggregating
218
- @aggregator.add metric
219
- else
220
- @transport.call(metric)
221
- end
222
- end
223
-
224
- def expand_name(name)
225
- # Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
226
- name = name.to_s.gsub('::', '.').tr(':|@', '_')
227
- "#{prefix}#{name}#{postfix}"
228
- end
229
-
230
- def udp_transport(metric)
231
- #puts "socket < #{metric}\n"
232
- self.class.logger.debug { "Statsd: #{metric}" } if self.class.logger
233
- socket.send(metric.to_s, 0, @host, @port)
234
- rescue => boom
235
- #puts "socket send error"
236
- @dropped +=1
237
- self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
238
- nil
239
- end
240
-
241
- STATSD_SEVERITY = 3
242
- def mq_transport(metric)
243
- #puts "MQ < #{metric}\n" #debug
244
- self.class.logger.debug { "Statsd: #{metric}" } if self.class.logger
245
- if not @mq
246
- begin
247
- @mq = POSIX_MQ.new("/afcollectorapi", Fcntl::O_WRONLY | Fcntl::O_NONBLOCK)
248
- rescue => boom
249
- self.class.logger.debug { "Statsd: MQ open error #{boom.class} #{boom}" } if self.class.logger
250
- # failed to open MQ. Fall back to UPD transport. Note: Current message will be lost.
251
- @dropped += 1
252
- # puts "fallback to udp"
253
- set_transport :udp_transport
254
- return nil
255
- end
256
- end
257
- begin
258
- @mq.send(metric.to_s, STATSD_SEVERITY)
259
- rescue => boom
260
- # just drop it on the floor
261
- @dropped += 1
262
- #puts "MQ send error: #{boom.class} #{boom}"
263
- self.class.logger.error { "Statsd: MQ Send Error#{boom.class} #{boom}" } if self.class.logger
264
- nil
265
- end
266
- end
267
-
268
- def both_transport(metric)
269
- mq_transport(metric)
270
- udp_transport(metric)
271
- end
272
-
273
- private
274
-
275
- def socket
276
- Thread.current[:statsd_socket] ||= UDPSocket.new
277
- end
278
-
279
- end # class Statsd
280
-
281
-
1
+ require 'socket'
2
+ require 'forwardable'
3
+ require 'rubygems'
4
+ require 'posix_mq'
5
+ require 'afstatsd/statsd_metrics'
6
+ require 'afstatsd/statsd_aggregator'
7
+ require 'monitor'
8
+ require 'fcntl'
9
+
10
+ # = Statsd: A Statsd client (https://github.com/etsy/statsd)
11
+ #
12
+ # @example Set up a global Statsd client for a server on localhost:9125,
13
+ # aggregate 20 seconds worth of metrics
14
+ # $statsd = Statsd.new 'localhost', 8125, 20
15
+ # @example Send some stats
16
+ # $statsd.increment 'garets'
17
+ # $statsd.timing 'glork', 320
18
+ # $statsd.gauge 'bork', 100
19
+ # @example Use {#time} to time the execution of a block
20
+ # $statsd.time('account.activate') { @account.activate! }
21
+ # @example Create a namespaced statsd client and increment 'account.activate'
22
+ # statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
23
+ # statsd.increment 'activate'
24
+ #
25
+ # Statsd instances are thread safe for general usage, by using a thread local
26
+ # UDPSocket and carrying no state. The attributes are stateful, and are not
27
+ # mutexed, it is expected that users will not change these at runtime in
28
+ # threaded environments. If users require such use cases, it is recommend that
29
+ # users either mutex around their Statsd object, or create separate objects for
30
+ # each namespace / host+port combination.
31
+ class Statsd
32
+
33
+ # A namespace to prepend to all statsd calls.
34
+ attr_reader :namespace
35
+
36
+ # StatsD host. Defaults to 127.0.0.1. Only used with UDP transport
37
+ attr_reader :host
38
+
39
+ # StatsD port. Defaults to 8125. Only used with UDP transport
40
+ attr_reader :port
41
+
42
+ # StatsD namespace prefix, generated from #namespace
43
+ attr_reader :prefix
44
+
45
+ # a postfix to append to all metrics
46
+ attr_reader :postfix
47
+
48
+ # count of messages that were dropped due to transmit error
49
+ attr_reader :dropped
50
+
51
+ class << self
52
+ # Set to a standard logger instance to enable debug logging.
53
+ attr_accessor :logger
54
+ end
55
+
56
+ # @param [String] host your statsd host
57
+ # @param [Integer] port your statsd port
58
+ # @param [Integer] interval for aggregatore
59
+ def initialize(host = '127.0.0.1', port = 8125, interval = 20)
60
+ self.host, self.port = host, port
61
+ @prefix = nil
62
+ @postfix = nil
63
+ @aggregator = StatsdAggregator.new(interval)
64
+ set_transport :mq_transport
65
+ self.aggregating = true unless interval == 0
66
+ @dropped = 0
67
+ end
68
+
69
+ # @param [method] The ruby symbol for the method that gets called to send
70
+ # one metric to the server. eg: set_transport :udp_transport
71
+ def set_transport(transport)
72
+ @transport = method(transport)
73
+ @aggregator.transport = @transport # aggregator needs to know
74
+ end
75
+
76
+ # @param [Boolean] Turn aggregation on or off
77
+ def aggregating= (should_aggregate)
78
+ if should_aggregate
79
+ @aggregator.start(@transport)
80
+ else
81
+ @aggregator.stop
82
+ end
83
+ end
84
+
85
+ # is the aggregator running?
86
+ def aggregating
87
+ @aggregator.running
88
+ end
89
+
90
+ # @attribute [w] namespace
91
+ # Writes are not thread safe.
92
+ def namespace=(namespace)
93
+ @namespace = namespace
94
+ @prefix = "#{namespace}."
95
+ end
96
+
97
+ # @attribute [w] postfix
98
+ # A value to be appended to the stat name after a '.'. If the value is
99
+ # blank then the postfix will be reset to nil (rather than to '.').
100
+ def postfix=(pf)
101
+ case pf
102
+ when nil, false, '' then @postfix = nil
103
+ else @postfix = ".#{pf}"
104
+ end
105
+ end
106
+
107
+ # @attribute [w] host
108
+ # Writes are not thread safe.
109
+ def host=(host)
110
+ @host = host || '127.0.0.1'
111
+ end
112
+
113
+ # @attribute [w] port
114
+ # Writes are not thread safe.
115
+ def port=(port)
116
+ @port = port || 8125
117
+ end
118
+
119
+ # Sends an increment (count = 1) for the given stat to the statsd server.
120
+ #
121
+ # @param [String] stat stat name
122
+ # @param [Numeric] sample_rate sample rate, 1 for always
123
+ # @param [String] optional note (AppFirst extension to StatsD)
124
+ # @see #count
125
+ def increment(stat, sample_rate=1, note="")
126
+ count stat, 1, sample_rate, note
127
+ end
128
+
129
+ # Sends a decrement (count = -1) for the given stat to the statsd server.
130
+ #
131
+ # @param [String] stat stat name
132
+ # @param [Numeric] sample_rate sample rate, 1 for always
133
+ # @param [String] optional note (AppFirst extension to StatsD)
134
+ # @see #count
135
+ def decrement(stat, sample_rate=1, note="")
136
+ count stat, -1, sample_rate, note
137
+ end
138
+
139
+ # Sends an arbitrary count for the given stat to the statsd server.
140
+ #
141
+ # @param [String] stat stat name
142
+ # @param [Integer] count count
143
+ # @param [Numeric] sample_rate sample rate, 1 for always
144
+ # @param [String] optional note (AppFirst extension to StatsD)
145
+ def count(stat, count, sample_rate=1, note="")
146
+ if sample_rate == 1 or rand < sample_rate
147
+ send_metric StatsdMetrics::CMetric.new(expand_name(stat), count, sample_rate, note)
148
+ end
149
+ end
150
+
151
+ # Sends an arbitary gauge value for the given stat to the statsd server.
152
+ #
153
+ # This is useful for recording things like available disk space,
154
+ # memory usage, and the like, which have different semantics than
155
+ # counters.
156
+ #
157
+ # @param [String] stat stat name.
158
+ # @param [Numeric] value gauge value.
159
+ # @param [String] optional note (AppFirst extension to StatsD)
160
+ # @example Report the current user count:
161
+ # $statsd.gauge('user.count', User.count)
162
+ def gauge(stat, value, note="")
163
+ send_metric StatsdMetrics::GMetric.new(expand_name(stat), value, note)
164
+ end
165
+
166
+ # Sends an arbitary set value for the given stat to the statsd server.
167
+ #
168
+ # This is for recording counts of unique events, which are useful to
169
+ # see on graphs to correlate to other values. For example, a deployment
170
+ # might get recorded as a set, and be drawn as annotations on a CPU history
171
+ # graph.
172
+ #
173
+ # @param [String] stat stat name.
174
+ # @param [Numeric] value event value.
175
+ # @param [String] optional note (AppFirst extension to StatsD)
176
+ # @example Report a deployment happening:
177
+ # $statsd.set('deployment', DEPLOYMENT_EVENT_CODE)
178
+ def set(stat, value, note="")
179
+ send_metric StatsdMetrics::SMetric.new(expand_name(stat), value, note)
180
+ end
181
+
182
+ # Sends a timing (in ms) for the given stat to the statsd server. The
183
+ # sample_rate determines what percentage of the time this report is sent. The
184
+ # statsd server then uses the sample_rate to correctly track the average
185
+ # timing for the stat.
186
+ #
187
+ # @param [String] stat stat name
188
+ # @param [Integer] ms timing in milliseconds
189
+ # @param [Numeric] sample_rate sample rate, 1 for always
190
+ # @param [String] optional note (AppFirst extension to StatsD)
191
+ def timing(stat, ms, sample_rate=1, note="")
192
+ if sample_rate == 1 or rand < sample_rate
193
+ send_metric StatsdMetrics::TMetric.new(expand_name(stat), ms, sample_rate, note)
194
+ end
195
+ end
196
+
197
+ # Reports execution time of the provided block using {#timing}.
198
+ #
199
+ # @param [String] stat stat name
200
+ # @param [Numeric] sample_rate sample rate, 1 for always
201
+ # @param [String] optional note (AppFirst extension to StatsD)
202
+ # @yield The operation to be timed
203
+ # @see #timing
204
+ # @example Report the time (in ms) taken to activate an account
205
+ # $statsd.time('account.activate') { @account.activate! }
206
+ def time(stat, sample_rate=1, note="")
207
+ start = Time.now
208
+ result = yield
209
+ timing(stat, ((Time.now - start) * 1000).round, sample_rate, note)
210
+ result
211
+ end
212
+
213
+ protected
214
+
215
+ def send_metric(metric)
216
+ # All the metric types above funnel to here. We will send or aggregate.
217
+ if aggregating
218
+ @aggregator.add metric
219
+ else
220
+ @transport.call(metric)
221
+ end
222
+ end
223
+
224
+ def expand_name(name)
225
+ # Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
226
+ name = name.to_s.gsub('::', '.').tr(':|@', '_')
227
+ "#{prefix}#{name}#{postfix}"
228
+ end
229
+
230
+ def udp_transport(metric)
231
+ #puts "socket < #{metric}\n"
232
+ self.class.logger.debug { "Statsd: #{metric}" } if self.class.logger
233
+ socket.send(metric.to_s, 0, @host, @port)
234
+ rescue => boom
235
+ #puts "socket send error"
236
+ @dropped +=1
237
+ self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
238
+ nil
239
+ end
240
+
241
+ STATSD_SEVERITY = 3
242
+ def mq_transport(metric)
243
+ #puts "MQ < #{metric}\n" #debug
244
+ self.class.logger.debug { "Statsd: #{metric}" } if self.class.logger
245
+ if not @mq
246
+ begin
247
+ @mq = POSIX_MQ.new("/afcollectorapi", Fcntl::O_WRONLY | Fcntl::O_NONBLOCK)
248
+ rescue => boom
249
+ self.class.logger.debug { "Statsd: MQ open error #{boom.class} #{boom}" } if self.class.logger
250
+ # failed to open MQ. Fall back to UPD transport. Note: Current message will be lost.
251
+ @dropped += 1
252
+ # puts "fallback to udp"
253
+ set_transport :udp_transport
254
+ return nil
255
+ end
256
+ end
257
+ begin
258
+ @mq.send(metric.to_s, STATSD_SEVERITY)
259
+ rescue => boom
260
+ # just drop it on the floor
261
+ @dropped += 1
262
+ #puts "MQ send error: #{boom.class} #{boom}"
263
+ self.class.logger.error { "Statsd: MQ Send Error#{boom.class} #{boom}" } if self.class.logger
264
+ nil
265
+ end
266
+ end
267
+
268
+ def both_transport(metric)
269
+ mq_transport(metric)
270
+ udp_transport(metric)
271
+ end
272
+
273
+ private
274
+
275
+ def socket
276
+ Thread.current[:statsd_socket] ||= UDPSocket.new
277
+ end
278
+
279
+ end # class Statsd
280
+
281
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: afstatsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-18 00:00:00.000000000 Z
12
+ date: 2013-03-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: posix_mq