multimeter 1.0.0.pre2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ ...