multimeter 1.0.0.pre2-java

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.
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Multimeter
4
+ VERSION = '1.0.0.pre2'
5
+ end
data/lib/multimeter.rb ADDED
@@ -0,0 +1,563 @@
1
+ # encoding: utf-8
2
+
3
+ $: << File.expand_path('../../../jar-gems/slf4j-jars/lib', __FILE__)
4
+ $: << File.expand_path('../../../jar-gems/metrics-core-jars/lib', __FILE__)
5
+
6
+ require 'metrics-core-jars'
7
+ require 'json'
8
+
9
+
10
+ module Yammer
11
+ module Metrics
12
+ import 'com.yammer.metrics.core.MetricsRegistry'
13
+ import 'com.yammer.metrics.core.MetricName'
14
+ import 'com.yammer.metrics.core.Meter'
15
+ import 'com.yammer.metrics.core.Counter'
16
+ import 'com.yammer.metrics.core.Histogram'
17
+ import 'com.yammer.metrics.core.Gauge'
18
+ import 'com.yammer.metrics.core.Timer'
19
+ import 'com.yammer.metrics.reporting.JmxReporter'
20
+
21
+ class Meter
22
+ def type
23
+ :meter
24
+ end
25
+
26
+ def to_h
27
+ {
28
+ :type => :meter,
29
+ :event_type => event_type,
30
+ :count => count,
31
+ :mean_rate => mean_rate,
32
+ :one_minute_rate => one_minute_rate,
33
+ :five_minute_rate => five_minute_rate,
34
+ :fifteen_minute_rate => fifteen_minute_rate
35
+ }
36
+ end
37
+ end
38
+
39
+ class Counter
40
+ def type
41
+ :counter
42
+ end
43
+
44
+ def to_h
45
+ {
46
+ :type => :counter,
47
+ :count => count
48
+ }
49
+ end
50
+ end
51
+
52
+ class Histogram
53
+ def type
54
+ :histogram
55
+ end
56
+
57
+ def to_h
58
+ {
59
+ :type => :histogram,
60
+ :count => count,
61
+ :max => max,
62
+ :min => min,
63
+ :mean => mean,
64
+ :std_dev => std_dev,
65
+ :sum => sum
66
+ }
67
+ end
68
+ end
69
+
70
+ class Gauge
71
+ def type
72
+ :gauge
73
+ end
74
+
75
+ def to_h
76
+ {
77
+ :type => :gauge,
78
+ :value => value
79
+ }
80
+ end
81
+ end
82
+
83
+ class Timer
84
+ def type
85
+ :timer
86
+ end
87
+
88
+ def to_h
89
+ {
90
+ :type => :timer,
91
+ :event_type => event_type,
92
+ :count => count,
93
+ :mean_rate => mean_rate,
94
+ :one_minute_rate => one_minute_rate,
95
+ :five_minute_rate => five_minute_rate,
96
+ :fifteen_minute_rate => fifteen_minute_rate,
97
+ :max => max,
98
+ :min => min,
99
+ :mean => mean,
100
+ :std_dev => std_dev,
101
+ :sum => sum
102
+ }
103
+ end
104
+
105
+ def measure
106
+ ctx = self.time
107
+ begin
108
+ yield
109
+ ensure
110
+ ctx.stop
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ module JavaConcurrency
118
+ import 'java.util.concurrent.TimeUnit'
119
+ import 'java.util.concurrent.ConcurrentHashMap'
120
+ import 'java.util.concurrent.atomic.AtomicReference'
121
+ import 'java.lang.Thread'
122
+ end
123
+
124
+ module Multimeter
125
+ def self.global_registry
126
+ GLOBAL_REGISTRY
127
+ end
128
+
129
+ def self.registry(group, scope, instance_id=nil)
130
+ Registry.new(group, scope, instance_id)
131
+ end
132
+
133
+ def self.metrics(group, scope, &block)
134
+ Class.new do
135
+ include(Metrics)
136
+ group(group)
137
+ scope(scope)
138
+ instance_eval(&block)
139
+ end.new
140
+ end
141
+
142
+ module Metrics
143
+ def self.included(m)
144
+ m.extend(Dsl)
145
+ end
146
+
147
+ def initialize(*args)
148
+ super
149
+ self.class.instance_gauges.each do |name, block|
150
+ instance_block = proc { instance_exec(&block) }
151
+ multimeter_registry.gauge(name, &instance_block)
152
+ end
153
+ self.class.instance_metrics.each do |type, name, options|
154
+ multimeter_registry.send(type, name, options)
155
+ end
156
+ end
157
+
158
+ def multimeter_registry
159
+ registry_mode = self.class.send(:registry_mode)
160
+ case registry_mode
161
+ when :instance, :linked_instance
162
+ @multimeter_registry ||= begin
163
+ package, _, class_name = self.class.name.rpartition('::')
164
+ group = self.class.send(:group) || package
165
+ scope = self.class.send(:scope) || class_name
166
+ if (iid_proc = self.class.send(:instance_id))
167
+ instance_id = instance_exec(&iid_proc)
168
+ else
169
+ instance_id = self.object_id
170
+ end
171
+ if registry_mode == :linked_instance
172
+ ::Multimeter.global_registry.sub_registry(scope, instance_id)
173
+ else
174
+ ::Multimeter.registry(group, scope, instance_id)
175
+ end
176
+ end
177
+ when :global
178
+ ::Multimeter.global_registry
179
+ else
180
+ self.class.multimeter_registry
181
+ end
182
+ end
183
+
184
+ module Dsl
185
+ def multimeter_registry
186
+ @multimeter_registry ||= begin
187
+ package, _, class_name = self.name.rpartition('::')
188
+ g = group || package
189
+ s = scope || class_name
190
+ case registry_mode
191
+ when :linked
192
+ ::Multimeter.global_registry.sub_registry(s)
193
+ when :global
194
+ ::Multimeter.global_registry
195
+ else
196
+ ::Multimeter.registry(g, s)
197
+ end
198
+ end
199
+ end
200
+
201
+ def instance_gauges
202
+ @instance_gauges || []
203
+ end
204
+
205
+ def instance_metrics
206
+ @instance_metrics || []
207
+ end
208
+
209
+ private
210
+
211
+ def group(g=nil)
212
+ @multimeter_registry_group = g.to_s if g
213
+ @multimeter_registry_group
214
+ end
215
+
216
+ def scope(t=nil)
217
+ @multimeter_registry_scope = t.to_s if t
218
+ @multimeter_registry_scope
219
+ end
220
+
221
+ def instance_id(pr=nil, &block_pr)
222
+ pr ||= block_pr
223
+ @multimeter_registry_iid = pr if pr
224
+ @multimeter_registry_iid
225
+ end
226
+
227
+ def registry_mode(m=nil)
228
+ @multimeter_registry_mode = m if m
229
+ @multimeter_registry_mode
230
+ end
231
+
232
+ def add_instance_gauge(name, block)
233
+ @instance_gauges ||= []
234
+ @instance_gauges << [name, block]
235
+ end
236
+
237
+ def add_instance_metric(type, name, options)
238
+ @instance_metrics ||= []
239
+ @instance_metrics << [type, name, options]
240
+ end
241
+
242
+ %w[counter meter histogram timer].each do |t|
243
+ type = t.to_sym
244
+ define_method(type) do |name, options={}|
245
+ case registry_mode
246
+ when :instance, :linked_instance
247
+ add_instance_metric(type, name, options)
248
+ else
249
+ multimeter_registry.send(type, name, options)
250
+ end
251
+ define_method(name) do
252
+ multimeter_registry.get(name)
253
+ end
254
+ end
255
+ end
256
+
257
+ def gauge(name, &block)
258
+ case registry_mode
259
+ when :instance, :linked_instance
260
+ add_instance_gauge(name, block)
261
+ else
262
+ multimeter_registry.gauge(name, &block)
263
+ end
264
+ define_method(name) do
265
+ multimeter_registry.gauge(name)
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ module InstanceMetrics
272
+ def self.included(m)
273
+ m.send(:include, Metrics)
274
+ m.send(:registry_mode, :instance)
275
+ end
276
+ end
277
+
278
+ module GlobalMetrics
279
+ def self.included(m)
280
+ m.send(:include, Metrics)
281
+ m.send(:registry_mode, :global)
282
+ end
283
+ end
284
+
285
+ module LinkedMetrics
286
+ def self.included(m)
287
+ m.send(:include, Metrics)
288
+ m.send(:registry_mode, :linked)
289
+ end
290
+ end
291
+
292
+ module LinkedInstanceMetrics
293
+ def self.included(m)
294
+ m.send(:include, Metrics)
295
+ m.send(:registry_mode, :linked_instance)
296
+ end
297
+ end
298
+
299
+ module Jmx
300
+ def jmx!
301
+ ::Yammer::Metrics::JmxReporter.start_default(@registry)
302
+ end
303
+ end
304
+
305
+ module Http
306
+ def http!(rack_handler, options={})
307
+ server_thread = JavaConcurrency::Thread.new do
308
+ rack_handler.run(create_app(self), options)
309
+ end
310
+ server_thread.daemon = true
311
+ server_thread.name = 'multimeter-http-server'
312
+ server_thread.start
313
+ end
314
+
315
+ private
316
+
317
+ class BadRequest < StandardError; end
318
+
319
+ JSON_HEADERS = {'Content-Type' => 'application/json'}.freeze
320
+ JSONP_HEADERS = {'Content-Type' => 'application/javascript'}.freeze
321
+ ERROR_HEADERS = {'Content-Type' => 'text/plain'}.freeze
322
+
323
+ def create_app(registry)
324
+ proc do |env|
325
+ begin
326
+ body = registry.to_h.to_json
327
+ headers = JSON_HEADERS
328
+ if (callback_name = env['QUERY_STRING'][/callback=([^$&]+)/, 1])
329
+ if callback_name =~ /^[\w\d]+$/
330
+ body = "#{callback_name}(#{body});"
331
+ headers = JSONP_HEADERS
332
+ else
333
+ raise BadRequest
334
+ end
335
+ end
336
+ [200, headers, [body]]
337
+ rescue BadRequest => e
338
+ [400, ERROR_HEADERS, ['Bad Request']]
339
+ rescue => e
340
+ [500, ERROR_HEADERS, ['Internal Server Error']]
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ class Registry
347
+ include Enumerable
348
+ include Jmx
349
+ include Http
350
+
351
+ attr_reader :group, :scope, :instance_id
352
+
353
+ def initialize(*args)
354
+ @group, @scope, @instance_id = args
355
+ @registry = ::Yammer::Metrics::MetricsRegistry.new
356
+ @sub_registries = JavaConcurrency::ConcurrentHashMap.new
357
+ end
358
+
359
+ def instance_registry?
360
+ !!@instance_id
361
+ end
362
+
363
+ def sub_registry(scope, instance_id=nil)
364
+ full_id = scope.dup
365
+ full_id << "/#{instance_id}" if instance_id
366
+ r = @sub_registries.get(full_id)
367
+ unless r
368
+ r = self.class.new(@group, scope, instance_id)
369
+ @sub_registries.put_if_absent(full_id, r)
370
+ r = @sub_registries.get(full_id)
371
+ end
372
+ r
373
+ end
374
+
375
+ def sub_registries
376
+ @sub_registries.values.to_a
377
+ end
378
+
379
+ def each_metric
380
+ return self unless block_given?
381
+ @registry.all_metrics.each do |metric_name, metric|
382
+ yield metric_name.name, metric
383
+ end
384
+ end
385
+ alias_method :each, :each_metric
386
+
387
+ def get(name)
388
+ @registry.all_metrics[create_name(name)]
389
+ end
390
+
391
+ def find_metric(name)
392
+ m = get(name)
393
+ unless m
394
+ sub_registries.each do |registry|
395
+ m = registry.find_metric(name)
396
+ break if m
397
+ end
398
+ end
399
+ m
400
+ end
401
+
402
+ def gauge(name, options={}, &block)
403
+ if get(name) && block_given?
404
+ raise ArgumentError, %(Cannot redeclare gauge #{name})
405
+ end
406
+ @registry.new_gauge(create_name(name), ProcGauge.new(block))
407
+ end
408
+
409
+ def counter(name, options={})
410
+ error_translation do
411
+ @registry.new_counter(create_name(name))
412
+ end
413
+ end
414
+
415
+ def meter(name, options={})
416
+ error_translation do
417
+ event_type = (options[:event_type] || '').to_s
418
+ time_unit = TIME_UNITS[options[:time_unit] || :seconds]
419
+ @registry.new_meter(create_name(name), event_type, time_unit)
420
+ end
421
+ end
422
+
423
+ def histogram(name, options={})
424
+ error_translation do
425
+ @registry.new_histogram(create_name(name), !!options[:biased])
426
+ end
427
+ end
428
+
429
+ def timer(name, options={})
430
+ error_translation do
431
+ duration_unit = TIME_UNITS[options[:duration_unit] || :milliseconds]
432
+ rate_unit = TIME_UNITS[options[:rate_unit] || :seconds]
433
+ @registry.new_timer(create_name(name), duration_unit, rate_unit)
434
+ end
435
+ end
436
+
437
+ def to_h
438
+ h = {@scope => {}}
439
+ each_metric do |metric_name, metric|
440
+ h[@scope][metric_name.to_sym] = metric.to_h
441
+ end
442
+ registries_by_scope = sub_registries.group_by { |r| r.scope }
443
+ registries_by_scope.each do |scope, registries|
444
+ if registries.size == 1
445
+ h.merge!(registries.first.to_h)
446
+ else
447
+ h[scope] = {}
448
+ registries_by_metric = Hash.new { |h, k| h[k] = [] }
449
+ registries.each do |registry|
450
+ registry.each_metric do |metric_name, _|
451
+ registries_by_metric[metric_name] << registry
452
+ end
453
+ end
454
+ registries_by_metric.each do |metric_name, registries|
455
+ if registries.size == 1
456
+ h[scope][metric_name.to_sym] = registries.first.get(metric_name).to_h
457
+ else
458
+ metrics_by_instance_id = Hash[registries.map { |r| [r.instance_id, r.get(metric_name)] }]
459
+ h[scope][metric_name.to_sym] = Aggregate.new(metrics_by_instance_id).to_h
460
+ end
461
+ end
462
+ end
463
+ h
464
+ end
465
+ h.delete_if { |k, v| v.empty? }
466
+ h
467
+ end
468
+
469
+ private
470
+
471
+ TIME_UNITS = {
472
+ :seconds => JavaConcurrency::TimeUnit::SECONDS,
473
+ :milliseconds => JavaConcurrency::TimeUnit::MILLISECONDS
474
+ }.freeze
475
+
476
+ def create_name(name)
477
+ ::Yammer::Metrics::MetricName.new(@group, @scope, name.to_s)
478
+ end
479
+
480
+ def error_translation
481
+ begin
482
+ yield
483
+ rescue java.lang.ClassCastException => cce
484
+ raise ArgumentError, %(Cannot redeclare a metric as another type)
485
+ end
486
+ end
487
+ end
488
+
489
+ class Aggregate
490
+ def initialize(metrics)
491
+ @metrics = metrics
492
+ @type = check_type!
493
+ end
494
+
495
+ def to_h
496
+ {
497
+ :type => :aggregate,
498
+ :total => compute_total,
499
+ :parts => Hash[@metrics.map { |k, v| [k.to_s, v.to_h] }]
500
+ }
501
+ end
502
+
503
+ private
504
+
505
+ def check_type!
506
+ types = @metrics.values.map(&:type).uniq
507
+ unless types.size == 1
508
+ raise ArgumentError, %[All metrics of an aggregate must be of the same type (they were: #{types.join(', ')})]
509
+ end
510
+ types.first
511
+ end
512
+
513
+ COMPUTATIONS = {
514
+ :max => :max,
515
+ :min => :min,
516
+ :type => :first,
517
+ :event_type => :first,
518
+ :count => :sum,
519
+ :sum => :sum,
520
+ :mean => :avg,
521
+ :mean_rate => :avg,
522
+ :one_minute_rate => :avg,
523
+ :five_minute_rate => :avg,
524
+ :fifteen_minute_rate => :avg,
525
+ :std_dev => :avg,
526
+ :value => :avg
527
+ }.freeze
528
+
529
+ def compute_total
530
+ h = {}
531
+ metric_hs = @metrics.values.map(&:to_h)
532
+ metric_hs.first.keys.each do |property|
533
+ values = metric_hs.map { |h| h[property] }
534
+ aggregate_value = begin
535
+ case COMPUTATIONS[property]
536
+ when :first then values.first
537
+ when :max then values.max
538
+ when :min then values.min
539
+ when :sum then values.reduce(:+)
540
+ when :avg then values.reduce(:+)/values.size.to_f
541
+ else
542
+ raise "Don't know how to aggregate #{property}"
543
+ end
544
+ end
545
+ h[property] = aggregate_value
546
+ end
547
+ h
548
+ end
549
+ end
550
+
551
+ class ProcGauge < ::Yammer::Metrics::Gauge
552
+ def initialize(proc)
553
+ super()
554
+ @proc = proc
555
+ end
556
+
557
+ def value
558
+ @proc.call
559
+ end
560
+ end
561
+
562
+ GLOBAL_REGISTRY = registry('global', 'global')
563
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multimeter
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: 6
5
+ version: 1.0.0.pre2
6
+ platform: java
7
+ authors:
8
+ - Theo Hultberg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-11 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: metrics-core-jars
16
+ version_requirements: &2058 !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ none: false
22
+ requirement: *2058
23
+ prerelease: false
24
+ type: :runtime
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ version_requirements: &2076 !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: '0'
32
+ none: false
33
+ requirement: *2076
34
+ prerelease: false
35
+ type: :runtime
36
+ description: Multimeter provides a JRuby DSL for instrumenting your application. It uses Yammer's Metrics library under the hood.
37
+ email:
38
+ - theo@iconara.net
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - lib/multimeter.rb
44
+ - lib/multimeter/version.rb
45
+ homepage: http://github.com/iconara/multimeter
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ none: false
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>'
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.1
62
+ none: false
63
+ requirements: []
64
+ rubyforge_project: multimeter
65
+ rubygems_version: 1.8.15
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: JRuby application metric instrumentation using Yammer's Metrics
69
+ test_files: []
70
+ ...